From 762bb8cd21bc31591dea5fdb56b0ba7e37c2920f Mon Sep 17 00:00:00 2001 From: William Tran-Viet Date: Fri, 22 Aug 2025 18:15:37 -0400 Subject: [PATCH 01/39] Update documentation --- libcxx/docs/FeatureTestMacroTable.rst | 2 ++ libcxx/docs/ReleaseNotes/22.rst | 1 + libcxx/docs/Status/Cxx2cPapers.csv | 2 +- libcxx/utils/generate_feature_test_macro_components.py | 1 + 4 files changed, 5 insertions(+), 1 deletion(-) diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst index 8fba6db871f08..0701c9015aff0 100644 --- a/libcxx/docs/FeatureTestMacroTable.rst +++ b/libcxx/docs/FeatureTestMacroTable.rst @@ -482,6 +482,8 @@ Status ---------------------------------------------------------- ----------------- ``__cpp_lib_not_fn`` ``202306L`` ---------------------------------------------------------- ----------------- + ``__cpp_lib_optional`` ``202506L`` + ---------------------------------------------------------- ----------------- ``__cpp_lib_optional_range_support`` ``202406L`` ---------------------------------------------------------- ----------------- ``__cpp_lib_out_ptr`` ``202311L`` diff --git a/libcxx/docs/ReleaseNotes/22.rst b/libcxx/docs/ReleaseNotes/22.rst index 25d33a9c2eb50..f8da79ed3666b 100644 --- a/libcxx/docs/ReleaseNotes/22.rst +++ b/libcxx/docs/ReleaseNotes/22.rst @@ -40,6 +40,7 @@ Implemented Papers - P2321R2: ``zip`` (`Github `__) (The paper is partially implemented. ``zip_transform_view`` is implemented in this release) +- P2988R12: ``std::optional`` (`Github `__) - P3044R2: sub-``string_view`` from ``string`` (`Github `__) - P3223R2: Making ``std::istream::ignore`` less surprising (`Github `__) - P3060R3: Add ``std::views::indices(n)`` (`Github `__) diff --git a/libcxx/docs/Status/Cxx2cPapers.csv b/libcxx/docs/Status/Cxx2cPapers.csv index a5423acf0d419..acadc9d6a9aff 100644 --- a/libcxx/docs/Status/Cxx2cPapers.csv +++ b/libcxx/docs/Status/Cxx2cPapers.csv @@ -122,7 +122,7 @@ "`P3293R3 `__","Splicing a base class subobject","2025-06 (Sofia)","","","`#148125 `__","" "`P3491R3 `__","``define_static_{string,object,array}``","2025-06 (Sofia)","","","`#148126 `__","" "`P3096R12 `__","Function Parameter Reflection in Reflection for C++26","2025-06 (Sofia)","","","`#148127 `__","" -"`P2988R12 `__","``std::optional``","2025-06 (Sofia)","","","`#148131 `__","" +"`P2988R12 `__","``std::optional``","2025-06 (Sofia)","|Complete|","22","`#148131 `__","" "`P3348R4 `__","C++26 should refer to C23 not C17","2025-06 (Sofia)","","","`#148133 `__","" "`P3037R6 `__","``constexpr`` ``std::shared_ptr`` and friends","2025-06 (Sofia)","","","`#148135 `__","" "`P3284R4 `__","``write_env`` and ``unstoppable`` Sender Adaptors","2025-06 (Sofia)","","","`#148136 `__","" diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py index f6f252751b3e3..51bfb35afc046 100644 --- a/libcxx/utils/generate_feature_test_macro_components.py +++ b/libcxx/utils/generate_feature_test_macro_components.py @@ -1006,6 +1006,7 @@ def add_version_header(tc): "c++17": 201606, "c++20": 202106, # P2231R1 Missing constexpr in std::optional and std::variant "c++23": 202110, # P0798R8 Monadic operations for std::optional + LWG3621 Remove feature-test macro __cpp_lib_monadic_optional + "c++26": 202506 # P2988R12: std::optional }, "headers": ["optional"], }, From 223ce4904dd0f79de432d274ea4fe790b5d015c1 Mon Sep 17 00:00:00 2001 From: William Tran-Viet Date: Fri, 22 Aug 2025 19:46:38 -0400 Subject: [PATCH 02/39] Update optional to allow T&, disable iterator and value_or for T(&)() and T(&)[] --- libcxx/include/__iterator/wrap_iter.h | 4 +- libcxx/include/optional | 125 ++++++++++++++++---------- 2 files changed, 82 insertions(+), 47 deletions(-) diff --git a/libcxx/include/__iterator/wrap_iter.h b/libcxx/include/__iterator/wrap_iter.h index d18d9682da449..98745f600a6ec 100644 --- a/libcxx/include/__iterator/wrap_iter.h +++ b/libcxx/include/__iterator/wrap_iter.h @@ -117,8 +117,8 @@ class __wrap_iter { friend class span; template friend struct array; - template - friend class optional; + template + friend struct __optional_iterator; }; template diff --git a/libcxx/include/optional b/libcxx/include/optional index ef1bfd3ec44c0..2c94a4a34da8c 100644 --- a/libcxx/include/optional +++ b/libcxx/include/optional @@ -412,9 +412,6 @@ struct __optional_storage_base : __optional_destruct_base<_Tp> { } }; -// optional is currently required to be ill-formed. However, it may -// be allowed in the future. For this reason, it has already been implemented -// to ensure we can make the change in an ABI-compatible manner. template struct __optional_storage_base<_Tp, true> { using value_type = _Tp; @@ -607,19 +604,19 @@ struct __is_std_optional : false_type {}; template struct __is_std_optional> : true_type {}; -template -class _LIBCPP_DECLSPEC_EMPTY_BASES optional - : private __optional_move_assign_base<_Tp>, - private __optional_sfinae_ctor_base_t<_Tp>, - private __optional_sfinae_assign_base_t<_Tp> { - using __base _LIBCPP_NODEBUG = __optional_move_assign_base<_Tp>; +template +struct __optional_iterator {}; - using __pointer _LIBCPP_NODEBUG = std::add_pointer_t<_Tp>; - using __const_pointer _LIBCPP_NODEBUG = std::add_pointer_t; +template +struct __optional_iterator< + _Tp, + enable_if_t && is_function_v<__libcpp_remove_reference_t<_Tp>>) && + !(is_lvalue_reference_v<_Tp> && is_array_v<__libcpp_remove_reference_t<_Tp>>)> > { +private: + using __pointer _LIBCPP_NODEBUG = std::add_pointer_t>; + using __const_pointer _LIBCPP_NODEBUG = std::add_pointer_t>; public: - using value_type = _Tp; - # if _LIBCPP_STD_VER >= 26 # ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL using iterator = __bounded_iter<__wrap_iter<__pointer>>; @@ -628,18 +625,66 @@ public: using iterator = __wrap_iter<__pointer>; using const_iterator = __wrap_iter<__const_pointer>; # endif + + // [optional.iterators], iterator support + _LIBCPP_HIDE_FROM_ABI constexpr iterator begin() noexcept { + auto& derived_self = static_cast&>(*this); +# ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL + return std::__make_bounded_iter( + std::__wrap_iter<__pointer>(std::addressof(derived_self.__get())), + std::__wrap_iter<__pointer>(std::addressof(derived_self.__get())), + std::__wrap_iter<__pointer>(std::addressof(derived_self.__get()) + (derived_self.has_value() ? 1 : 0))); +# else + return iterator(std::addressof(derived_self.__get())); +# endif + } + + _LIBCPP_HIDE_FROM_ABI constexpr const_iterator begin() const noexcept { + auto& derived_self = static_cast&>(*this); +# ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL + return std::__make_bounded_iter( + std::__wrap_iter<__const_pointer>(std::addressof(derived_self.__get())), + std::__wrap_iter<__const_pointer>(std::addressof(derived_self.__get())), + std::__wrap_iter<__const_pointer>(std::addressof(derived_self.__get()) + (derived_self.has_value() ? 1 : 0))); +# else + return const_iterator(std::addressof(derived_self.__get())); +# endif + } + + _LIBCPP_HIDE_FROM_ABI constexpr iterator end() noexcept { + return begin() + (static_cast&>(*this).has_value() ? 1 : 0); + } + _LIBCPP_HIDE_FROM_ABI constexpr const_iterator end() const noexcept { + return begin() + (static_cast&>(*this).has_value() ? 1 : 0); + } # endif +}; + +template +class _LIBCPP_DECLSPEC_EMPTY_BASES optional + : private __optional_move_assign_base<_Tp>, + private __optional_sfinae_ctor_base_t<_Tp>, + private __optional_sfinae_assign_base_t<_Tp>, + public __optional_iterator<_Tp> { + using __base _LIBCPP_NODEBUG = __optional_move_assign_base<_Tp>; + +public: + using value_type = _Tp; + using __trivially_relocatable _LIBCPP_NODEBUG = conditional_t<__libcpp_is_trivially_relocatable<_Tp>::value, optional, void>; using __replaceable _LIBCPP_NODEBUG = conditional_t<__is_replaceable_v<_Tp>, optional, void>; private: - // Disable the reference extension using this static assert. static_assert(!is_same_v<__remove_cvref_t, in_place_t>, "instantiation of optional with in_place_t is ill-formed"); static_assert(!is_same_v<__remove_cvref_t, nullopt_t>, "instantiation of optional with nullopt_t is ill-formed"); - static_assert(!is_reference_v, "instantiation of optional with a reference type is ill-formed"); +# if _LIBCPP_STD_VER >= 26 + static_assert(!is_rvalue_reference_v<_Tp>, "instantiation of optional with an rvalue reference type is ill-formed"); +# else + static_assert(!is_reference_v<_Tp>, "instantiation of optional with a reference type is ill-formed"); +# endif static_assert(is_destructible_v, "instantiation of optional with a non-destructible type is ill-formed"); static_assert(!is_array_v, "instantiation of optional with an array type is ill-formed"); @@ -833,34 +878,6 @@ public: } } -# if _LIBCPP_STD_VER >= 26 - // [optional.iterators], iterator support - _LIBCPP_HIDE_FROM_ABI constexpr iterator begin() noexcept { -# ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL - return std::__make_bounded_iter( - std::__wrap_iter<__pointer>(std::addressof(this->__get())), - std::__wrap_iter<__pointer>(std::addressof(this->__get())), - std::__wrap_iter<__pointer>(std::addressof(this->__get()) + (this->has_value() ? 1 : 0))); -# else - return iterator(std::addressof(this->__get())); -# endif - } - - _LIBCPP_HIDE_FROM_ABI constexpr const_iterator begin() const noexcept { -# ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL - return std::__make_bounded_iter( - std::__wrap_iter<__const_pointer>(std::addressof(this->__get())), - std::__wrap_iter<__const_pointer>(std::addressof(this->__get())), - std::__wrap_iter<__const_pointer>(std::addressof(this->__get()) + (this->has_value() ? 1 : 0))); -# else - return const_iterator(std::addressof(this->__get())); -# endif - } - - _LIBCPP_HIDE_FROM_ABI constexpr iterator end() noexcept { return begin() + (this->has_value() ? 1 : 0); } - _LIBCPP_HIDE_FROM_ABI constexpr const_iterator end() const noexcept { return begin() + (this->has_value() ? 1 : 0); } -# endif - _LIBCPP_HIDE_FROM_ABI constexpr add_pointer_t operator->() const noexcept { _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator-> called on a disengaged value"); return std::addressof(this->__get()); @@ -920,20 +937,38 @@ public: return std::move(this->__get()); } - template > + template = remove_cv_t<_Tp>> +# if _LIBCPP_STD_VER >= 26 + requires(!(is_lvalue_reference_v<_Tp> && is_function_v<__libcpp_remove_reference_t<_Tp>>) && + !(is_lvalue_reference_v<_Tp> && is_array_v<__libcpp_remove_reference_t<_Tp>>)) +# endif _LIBCPP_HIDE_FROM_ABI constexpr value_type value_or(_Up&& __v) const& { static_assert(is_copy_constructible_v, "optional::value_or: T must be copy constructible"); static_assert(is_convertible_v<_Up, value_type>, "optional::value_or: U must be convertible to T"); return this->has_value() ? this->__get() : static_cast(std::forward<_Up>(__v)); } - template > + template = remove_cv_t<_Tp>> +# if _LIBCPP_STD_VER >= 26 + requires(!is_lvalue_reference_v<_Tp>) +# endif _LIBCPP_HIDE_FROM_ABI constexpr value_type value_or(_Up&& __v) && { static_assert(is_move_constructible_v, "optional::value_or: T must be move constructible"); static_assert(is_convertible_v<_Up, value_type>, "optional::value_or: U must be convertible to T"); return this->has_value() ? std::move(this->__get()) : static_cast(std::forward<_Up>(__v)); } +# if _LIBCPP_STD_VER >= 26 + template > + requires(is_lvalue_reference_v<_Tp> && + !(is_function_v<__libcpp_remove_reference_t<_Tp>> || is_array_v<__libcpp_remove_reference_t<_Tp>>)) + _LIBCPP_HIDE_FROM_ABI constexpr value_type value_or(_Up&& __v) && { + static_assert(is_move_constructible_v, "optional::value_or: T must be move constructible"); + static_assert(is_convertible_v<_Up, value_type>, "optional::value_or: U must be convertible to T"); + return this->has_value() ? this->__get() : static_cast(std::forward<_Up>(__v)); + } +# endif + # if _LIBCPP_STD_VER >= 23 template _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) & { From 72bb6e9b5b5681235998a0f6aba102d31014e6a4 Mon Sep 17 00:00:00 2001 From: William Tran-Viet Date: Sat, 23 Aug 2025 00:51:45 -0400 Subject: [PATCH 03/39] Augment tests --- .../iterator.compile.pass.cpp | 9 +- .../value_or.compile.pass.cpp | 27 ++++ .../optional/optional.iterator/begin.pass.cpp | 7 +- .../optional/optional.iterator/end.pass.cpp | 8 +- .../optional.iterator/iterator.pass.cpp | 27 ++-- .../optional.monadic/and_then.pass.cpp | 80 +++++++++++ .../optional.monadic/or_else.pass.cpp | 26 ++++ .../optional.monadic/transform.pass.cpp | 128 ++++++++++++++++-- .../assign_value.pass.cpp | 36 ++++- .../optional.object.assign/emplace.pass.cpp | 25 +++- .../optional.object.ctor/ctor.verify.cpp | 4 + .../optional.object.ctor/ref_t.pass.cpp | 40 ++++++ .../optional.object.dtor/dtor.pass.cpp | 6 + .../optional.object.mod/reset.pass.cpp | 11 ++ .../dereference.pass.cpp | 14 +- .../dereference_const.pass.cpp | 19 +++ .../has_value.pass.cpp | 9 +- .../optional.object.observe/op_arrow.pass.cpp | 25 ++++ .../op_arrow_const.pass.cpp | 19 +++ .../optional.object.observe/value.pass.cpp | 8 ++ .../optional.object.observe/value_or.pass.cpp | 8 ++ .../value_or_const.pass.cpp | 8 +- ...al_requires_destructible_object.verify.cpp | 13 +- 23 files changed, 519 insertions(+), 38 deletions(-) create mode 100644 libcxx/test/libcxx/utilities/optional/optional.object/optional.object.observe/value_or.compile.pass.cpp create mode 100644 libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/ref_t.pass.cpp diff --git a/libcxx/test/libcxx/utilities/optional/optional.iterator/iterator.compile.pass.cpp b/libcxx/test/libcxx/utilities/optional/optional.iterator/iterator.compile.pass.cpp index 3cdd7553e2e5d..472049a91a8d1 100644 --- a/libcxx/test/libcxx/utilities/optional/optional.iterator/iterator.compile.pass.cpp +++ b/libcxx/test/libcxx/utilities/optional/optional.iterator/iterator.compile.pass.cpp @@ -23,8 +23,7 @@ concept has_iterator_aliases = requires { static_assert(has_iterator_aliases>); static_assert(has_iterator_aliases>); - -// TODO: Uncomment these once P2988R12 is implemented, as they would be testing optional - -// static_assert(!has_iterator_aliases>); -// static_assert(!has_iterator_aliases>); +static_assert(has_iterator_aliases>); +static_assert(has_iterator_aliases>); +static_assert(!has_iterator_aliases>); +static_assert(!has_iterator_aliases>); diff --git a/libcxx/test/libcxx/utilities/optional/optional.object/optional.object.observe/value_or.compile.pass.cpp b/libcxx/test/libcxx/utilities/optional/optional.object/optional.object.observe/value_or.compile.pass.cpp new file mode 100644 index 0000000000000..298e2f6f55d4d --- /dev/null +++ b/libcxx/test/libcxx/utilities/optional/optional.object/optional.object.observe/value_or.compile.pass.cpp @@ -0,0 +1,27 @@ + +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++26 + +// + +// template T optional::value_or(U&&); + +#include + +template +concept has_value_or = requires(Opt opt, T&& t) { + { opt.value_or(t) } -> std::same_as; +}; + +static_assert(has_value_or, int>); +static_assert(has_value_or, int&>); +static_assert(has_value_or, const int&>); +static_assert(!has_value_or&&, int(&)[1]>); +static_assert(!has_value_or&&, int(&)()>); diff --git a/libcxx/test/std/utilities/optional/optional.iterator/begin.pass.cpp b/libcxx/test/std/utilities/optional/optional.iterator/begin.pass.cpp index df95a8df3793f..81234525923a1 100644 --- a/libcxx/test/std/utilities/optional/optional.iterator/begin.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.iterator/begin.pass.cpp @@ -21,7 +21,8 @@ template constexpr bool test() { - std::optional opt{T{}}; + std::remove_reference_t t = std::remove_reference_t{}; + std::optional opt{t}; { // begin() is marked noexcept static_assert(noexcept(opt.begin())); @@ -53,6 +54,10 @@ constexpr bool tests() { assert(test()); assert(test()); assert(test()); + assert(test()); + assert(test()); + assert(test()); + assert(test()); return true; } diff --git a/libcxx/test/std/utilities/optional/optional.iterator/end.pass.cpp b/libcxx/test/std/utilities/optional/optional.iterator/end.pass.cpp index 966c3e7441880..c62c9fc7746d6 100644 --- a/libcxx/test/std/utilities/optional/optional.iterator/end.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.iterator/end.pass.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include template @@ -41,7 +42,8 @@ constexpr bool test() { assert(it2 == std::as_const(disengaged).end()); } - std::optional engaged{T{}}; + std::remove_reference_t t = std::remove_reference_t{}; + std::optional engaged{t}; { // end() != begin() if the optional is engaged auto it = engaged.end(); @@ -62,6 +64,10 @@ constexpr bool tests() { assert(test()); assert(test()); assert(test()); + assert(test()); + assert(test()); + assert(test()); + assert(test()); return true; } diff --git a/libcxx/test/std/utilities/optional/optional.iterator/iterator.pass.cpp b/libcxx/test/std/utilities/optional/optional.iterator/iterator.pass.cpp index 1203290a0290a..34d0d6b135564 100644 --- a/libcxx/test/std/utilities/optional/optional.iterator/iterator.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.iterator/iterator.pass.cpp @@ -20,9 +20,10 @@ #include #include -template +template __val> constexpr bool test() { - std::optional opt{__val}; + std::remove_reference_t v{__val}; + std::optional opt{v}; { // Dereferencing an iterator of an engaged optional will return the same value that the optional holds. auto it = opt.begin(); @@ -41,13 +42,14 @@ constexpr bool test() { assert(std::random_access_iterator); } - { // const_iterator::value_type == std::remove_cv_t, const_iterator::reference == const T&, iterator::value_type = std::remove_cv_t, iterator::reference == T& + { // const_iterator::value_type == std::remove_cvref_t, const_iterator::reference == const T&, iterator::value_type = std::remove_cvref_t, iterator::reference == T& + // std::remove_cv_t is impossible for optional auto it = opt.begin(); auto it2 = std::as_const(opt).begin(); - assert((std::is_same_v>)); - assert((std::is_same_v)); - assert((std::is_same_v>)); - assert((std::is_same_v)); + assert((std::is_same_v>)); + assert((std::is_same_v&>)); + assert((std::is_same_v>)); + assert((std::is_same_v&>)); } { // std::ranges::size for an engaged optional == 1, disengaged optional == 0 @@ -68,13 +70,13 @@ constexpr bool test() { // An optional with value that is reset will have a begin() == end(), then when it is reassigned a value, // begin() != end(), and *begin() will contain the new value. { - std::optional val{__val}; + std::optional val{v}; assert(val.begin() != val.end()); val.reset(); assert(val.begin() == val.end()); - val.emplace(__val); + val.emplace(v); assert(val.begin() != val.end()); - assert(*(val.begin()) == __val); + assert(*(val.begin()) == v); } return true; @@ -86,6 +88,11 @@ constexpr bool tests() { assert((test())); assert((test())); assert((test())); + assert((test())); + assert((test())); + assert((test())); + assert((test())); + assert((test())); return true; } diff --git a/libcxx/test/std/utilities/optional/optional.monadic/and_then.pass.cpp b/libcxx/test/std/utilities/optional/optional.monadic/and_then.pass.cpp index 97305d976e066..3d945e437151e 100644 --- a/libcxx/test/std/utilities/optional/optional.monadic/and_then.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.monadic/and_then.pass.cpp @@ -257,8 +257,88 @@ constexpr bool test() { return true; } +#if TEST_STD_VER >= 26 +constexpr bool test_ref() { + // Test & overload + { + // Without & qualifier on F's operator() + { + int j = 42; + std::optional i{j}; + assert(i.and_then(LVal{}) == 1); + assert(i.and_then(NOLVal{}) == std::nullopt); + ASSERT_SAME_TYPE(decltype(i.and_then(LVal{})), std::optional); + } + + //With & qualifier on F's operator() + { + int j = 42; + std::optional i{j}; + RefQual l{}; + assert(i.and_then(l) == 1); + NORefQual nl{}; + assert(i.and_then(nl) == std::nullopt); + ASSERT_SAME_TYPE(decltype(i.and_then(l)), std::optional); + } + } + + // Test const& overload + { + // Without & qualifier on F's operator() + { + int j = 42; + std::optional i{j}; + assert(i.and_then(CLVal{}) == 1); + assert(i.and_then(NOCLVal{}) == std::nullopt); + ASSERT_SAME_TYPE(decltype(i.and_then(CLVal{})), std::optional); + } + + //With & qualifier on F's operator() + { + int j = 42; + const std::optional i{j}; + const CRefQual l{}; + assert(i.and_then(l) == 1); + const NOCRefQual nl{}; + assert(i.and_then(nl) == std::nullopt); + ASSERT_SAME_TYPE(decltype(i.and_then(l)), std::optional); + } + } + + // Test && overload + { + + //With & qualifier on F's operator() + { + int j = 42; + std::optional i{j}; + assert(i.and_then(RVRefQual{}) == 1); + assert(i.and_then(NORVRefQual{}) == std::nullopt); + ASSERT_SAME_TYPE(decltype(i.and_then(RVRefQual{})), std::optional); + } + } + + // Test const&& overload + { + //With & qualifier on F's operator() + { + int j = 42; + const std::optional i{j}; + const RVCRefQual l{}; + assert(i.and_then(std::move(l)) == 1); + const NORVCRefQual nl{}; + assert(i.and_then(std::move(nl)) == std::nullopt); + ASSERT_SAME_TYPE(decltype(i.and_then(std::move(l))), std::optional); + } + } + return true; +} +#endif + int main(int, char**) { test(); static_assert(test()); + test_ref(); + static_assert(test_ref()); return 0; } diff --git a/libcxx/test/std/utilities/optional/optional.monadic/or_else.pass.cpp b/libcxx/test/std/utilities/optional/optional.monadic/or_else.pass.cpp index ccc94ab9be2cb..de0a67c1579ee 100644 --- a/libcxx/test/std/utilities/optional/optional.monadic/or_else.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.monadic/or_else.pass.cpp @@ -62,6 +62,32 @@ constexpr bool test() { return std::optional{}; }); } +#if TEST_STD_VER >= 26 + { + int i = 2; + std::optional opt; + assert(opt.or_else([&] { return std::optional{i}; }) == i); + int j = 3; + opt = j; + opt.or_else([] { + assert(false); + return std::optional{}; + }); + assert(opt == j); + } + { + int i = 2; + std::optional opt; + assert(std::move(opt).or_else([&] { return std::optional{i}; }) == i); + int j = 3; + opt = j; + std::move(opt).or_else([] { + assert(false); + return std::optional{}; + }); + assert(opt == j); + } +#endif return true; } diff --git a/libcxx/test/std/utilities/optional/optional.monadic/transform.pass.cpp b/libcxx/test/std/utilities/optional/optional.monadic/transform.pass.cpp index 0a151517b101c..0d14ceb3bda8c 100644 --- a/libcxx/test/std/utilities/optional/optional.monadic/transform.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.monadic/transform.pass.cpp @@ -19,60 +19,61 @@ #include #include #include +#include struct LVal { constexpr int operator()(int&) { return 1; } - int operator()(const int&) = delete; - int operator()(int&&) = delete; + int operator()(const int&) = delete; + int operator()(int&&) = delete; int operator()(const int&&) = delete; }; struct CLVal { int operator()(int&) = delete; constexpr int operator()(const int&) { return 1; } - int operator()(int&&) = delete; + int operator()(int&&) = delete; int operator()(const int&&) = delete; }; struct RVal { - int operator()(int&) = delete; + int operator()(int&) = delete; int operator()(const int&) = delete; constexpr int operator()(int&&) { return 1; } int operator()(const int&&) = delete; }; struct CRVal { - int operator()(int&) = delete; + int operator()(int&) = delete; int operator()(const int&) = delete; - int operator()(int&&) = delete; + int operator()(int&&) = delete; constexpr int operator()(const int&&) { return 1; } }; struct RefQual { constexpr int operator()(int) & { return 1; } - int operator()(int) const& = delete; - int operator()(int) && = delete; + int operator()(int) const& = delete; + int operator()(int) && = delete; int operator()(int) const&& = delete; }; struct CRefQual { int operator()(int) & = delete; constexpr int operator()(int) const& { return 1; } - int operator()(int) && = delete; + int operator()(int) && = delete; int operator()(int) const&& = delete; }; struct RVRefQual { - int operator()(int) & = delete; + int operator()(int) & = delete; int operator()(int) const& = delete; constexpr int operator()(int) && { return 1; } int operator()(int) const&& = delete; }; struct RVCRefQual { - int operator()(int) & = delete; + int operator()(int) & = delete; int operator()(int) const& = delete; - int operator()(int) && = delete; + int operator()(int) && = delete; constexpr int operator()(int) const&& { return 1; } }; @@ -83,7 +84,7 @@ struct NoCopy { }; struct NoMove { - NoMove() = default; + NoMove() = default; NoMove(NoMove&&) = delete; NoMove operator()(const NoCopy&&) { return NoMove{}; } }; @@ -200,8 +201,109 @@ constexpr bool test() { return true; } +constexpr bool test_ref() { + { + std::optional opt1; + auto opt1r = opt1.transform([](int i) { return i + 2; }); + assert(!opt1); + ASSERT_SAME_TYPE(decltype(opt1r), std::optional); + } + + { + int i = 42; + std::optional opt{i}; + auto o2 = opt.transform([](int j) { return j + 2; }); + assert(*o2 == 44); + ASSERT_SAME_TYPE(decltype(o2), std::optional); + } + // Test & overload + { + // Without & qualifier on F's operator() + { + int i = 42; + std::optional opt{i}; + auto o3 = opt.transform(LVal{}); + assert(*o3 == 1); + ASSERT_SAME_TYPE(decltype(o3), std::optional); + } + + //With & qualifier on F's operator() + { + int i = 42; + std::optional opt{i}; + RefQual l{}; + auto o3 = opt.transform(l); + assert(*o3 == 1); + ASSERT_SAME_TYPE(decltype(o3), std::optional); + } + } + // const& overload + { + // Without & qualifier on F's operator() + { + int i = 42; + std::optional opt{i}; + auto o3 = std::as_const(opt).transform(CLVal{}); + assert(*o3 == 1); + ASSERT_SAME_TYPE(decltype(o3), std::optional); + } + + //With & qualifier on F's operator() + { + int i = 42; + const std::optional opt{i}; + const CRefQual l{}; + auto o3 = opt.transform(l); + assert(*o3 == 1); + ASSERT_SAME_TYPE(decltype(o3), std::optional); + } + } + + // Test && overload + { + // Without & qualifier on F's operator() + { + int i = 42; + std::optional opt{i}; + auto o3 = std::move(opt).transform(RVal{}); + assert(*o3 == 1); + ASSERT_SAME_TYPE(decltype(o3), std::optional); + } + + //With & qualifier on F's operator() + { + int i = 42; + std::optional opt{i}; + auto o3 = std::move(opt).transform(RVRefQual{}); + assert(*o3 == 1); + ASSERT_SAME_TYPE(decltype(o3), std::optional); + } + } + + // const&& overload + { + //With & qualifier on F's operator() + { + int i = 42; + std::optional opt{i}; + const RVCRefQual rvc{}; + auto o3 = opt.transform(std::move(rvc)); + assert(*o3 == 1); + ASSERT_SAME_TYPE(decltype(o3), std::optional); + } + } + { + std::optional o6 = std::nullopt; + auto o6r = o6.transform([](int) { return 42; }); + assert(!o6r); + } + return true; +} + int main(int, char**) { test(); static_assert(test()); + test_ref(); + static_assert(test_ref()); return 0; } diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.assign/assign_value.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.assign/assign_value.pass.cpp index eaca111b72dca..d633f91dc6fa5 100644 --- a/libcxx/test/std/utilities/optional/optional.object/optional.object.assign/assign_value.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.assign/assign_value.pass.cpp @@ -250,6 +250,37 @@ constexpr T pr38638(T v) return *o + 2; } +#if TEST_STD_VER >= 26 + +template _Val> +constexpr void test_with_ref() { + T t{_Val}; + { // to empty + optional opt; + opt = t; + assert(static_cast(opt) == true); + assert(*opt == t); + } + { // to existing + optional opt{t}; + opt = t; + assert(static_cast(opt) == true); + assert(*opt == t); + } + { // test default argument + optional opt; + opt = {t}; + assert(static_cast(opt) == true); + assert(*opt == t); + } + { // test default argument + optional opt{t}; + opt = {}; + assert(static_cast(opt) == false); + } +} +#endif + int main(int, char**) { @@ -281,5 +312,8 @@ int main(int, char**) static_assert(pr38638(3) == 5, ""); - return 0; +#if TEST_STD_VER >= 26 + test_with_ref(); +#endif + return 0; } diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.assign/emplace.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.assign/emplace.pass.cpp index 245d8ff3d2146..1c921f74946ec 100644 --- a/libcxx/test/std/utilities/optional/optional.object/optional.object.assign/emplace.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.assign/emplace.pass.cpp @@ -221,6 +221,24 @@ TEST_CONSTEXPR_CXX20 bool test_empty_emplace() { return true; } +#if TEST_STD_VER >= 26 +template _Val> +constexpr bool test_ref() { + using Opt = std::optional; + T t{_Val}; + { + Opt opt; + auto & v = opt.emplace(t); + static_assert(std::is_same_v, ""); + assert(static_cast(opt) == true); + assert(*opt == t); + assert(&v == &*opt); + assert(&t == &*opt); + } + return true; +} +#endif + int main(int, char**) { { @@ -291,6 +309,11 @@ int main(int, char**) } } #endif - +#if TEST_STD_VER >= 26 + static_assert(test_ref()); + static_assert(test_ref()); + assert((test_ref())); + assert((test_ref())); +#endif return 0; } diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/ctor.verify.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/ctor.verify.cpp index 775d2bde7d13d..a484a26f45c0d 100644 --- a/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/ctor.verify.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/ctor.verify.cpp @@ -24,7 +24,11 @@ struct NonDestructible { ~NonDestructible() = delete; }; int main(int, char**) { { +#if TEST_STD_VER >= 26 + std::optional opt2; // expected-error-re@optional:* {{static assertion failed{{.*}}instantiation of optional with an rvalue reference type is ill-formed}} +#else std::optional o1; // expected-error-re@optional:* {{static assertion failed{{.*}}instantiation of optional with a reference type is ill-formed}} +#endif std::optional o2; // expected-error-re@optional:* {{static assertion failed{{.*}}instantiation of optional with a non-destructible type is ill-formed}} std::optional o3; // expected-error-re@optional:* {{static assertion failed{{.*}}instantiation of optional with an array type is ill-formed}} } diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/ref_t.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/ref_t.pass.cpp new file mode 100644 index 0000000000000..e3d225a8201bc --- /dev/null +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/ref_t.pass.cpp @@ -0,0 +1,40 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++26 + +// + +#include +#include +#include +#include + +template _Val> +constexpr bool test() { + std::remove_reference_t item{_Val}; + std::optional opt{item}; + + { + assert(*opt == item); + assert(&(*opt) == &item); + } + { + assert(*std::as_const(opt) == item); + assert(&(*std::as_const(opt)) == &item); + } + + return true; +} + +int main(int, char**) { + static_assert((test())); + static_assert((test())); + assert((test())); + assert((test())); +} diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.dtor/dtor.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.dtor/dtor.pass.cpp index c0044276ea9ad..cb9f757c2cc9b 100644 --- a/libcxx/test/std/utilities/optional/optional.object/optional.object.dtor/dtor.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.dtor/dtor.pass.cpp @@ -65,5 +65,11 @@ int main(int, char**) assert(X::dtor_called == true); } + { + typedef int& T; + static_assert(std::is_trivially_destructible::value, ""); + static_assert(std::is_trivially_destructible>::value, ""); + } + return 0; } diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.mod/reset.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.mod/reset.pass.cpp index 7029b37cbecd7..e633e3f1d8f37 100644 --- a/libcxx/test/std/utilities/optional/optional.object/optional.object.mod/reset.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.mod/reset.pass.cpp @@ -69,5 +69,16 @@ int main(int, char**) X::dtor_called = false; } +#if TEST_STD_VER >= 26 + { + X x{}; + optional opt(x); + X::dtor_called = false; + opt.reset(); + assert(X::dtor_called == false); + assert(static_cast(opt) == false); + } +#endif + return 0; } diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/dereference.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/dereference.pass.cpp index 49b4d21a28066..6c1bf8aa15a8d 100644 --- a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/dereference.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/dereference.pass.cpp @@ -50,7 +50,19 @@ int main(int, char**) optional opt(X{}); assert((*opt).test() == 4); } +#if TEST_STD_VER >= 26 + { + X x{}; + optional opt(x); + ASSERT_SAME_TYPE(decltype(*opt), X&); + ASSERT_NOEXCEPT(*opt); + } + { + X x{}; + optional opt(x); + assert((*opt).test() == 4); + } +#endif static_assert(test() == 7, ""); - return 0; } diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/dereference_const.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/dereference_const.pass.cpp index ff86d9534faf6..cbeb0c9e288e1 100644 --- a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/dereference_const.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/dereference_const.pass.cpp @@ -43,6 +43,25 @@ int main(int, char**) constexpr optional opt(X{}); static_assert((*opt).test() == 3, ""); } +#if TEST_STD_VER >= 26 + { + X x{}; + const optional opt{x}; + ASSERT_SAME_TYPE(decltype(*opt), X&); + ASSERT_NOEXCEPT(*opt); + } + { + X x{}; + const optional opt{x}; + ASSERT_SAME_TYPE(decltype(*opt), const X&); + ASSERT_NOEXCEPT(*opt); + } + { + static constexpr X x{}; + constexpr optional opt(x); + static_assert((*opt).test() == 3, ""); + } +#endif { constexpr optional opt(Y{}); assert((*opt).test() == 2); diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/has_value.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/has_value.pass.cpp index 6998e023022c5..038de500070fb 100644 --- a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/has_value.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/has_value.pass.cpp @@ -33,6 +33,13 @@ int main(int, char**) constexpr optional opt(0); static_assert(opt.has_value(), ""); } +#if TEST_STD_VER >= 26 + { + static constexpr int i = 0; + constexpr optional opt{i}; + static_assert(opt.has_value(), ""); + } +#endif - return 0; + return 0; } diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/op_arrow.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/op_arrow.pass.cpp index 2b5fba546ef42..ab5998feede3e 100644 --- a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/op_arrow.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/op_arrow.pass.cpp @@ -22,6 +22,7 @@ using std::optional; struct X { int test() noexcept {return 3;} + int test() const noexcept {return 3;} }; struct Y @@ -47,6 +48,30 @@ int main(int, char**) optional opt(X{}); assert(opt->test() == 3); } +#if TEST_STD_VER >= 26 + { + X x{}; + std::optional opt(x); + ASSERT_SAME_TYPE(decltype(opt.operator->()), X*); + ASSERT_NOEXCEPT(opt.operator->()); + } + { + X x{}; + std::optional opt(x); + ASSERT_SAME_TYPE(decltype(opt.operator->()), const X*); + ASSERT_NOEXCEPT(opt.operator->()); + } + { + X x{}; + optional opt{x}; + assert(opt->test() == 3); + } + { + X x{}; + optional opt{x}; + assert(opt->test() == 3); + } +#endif { static_assert(test() == 3, ""); } diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/op_arrow_const.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/op_arrow_const.pass.cpp index d8ce932bd7810..5154679cb6435 100644 --- a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/op_arrow_const.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/op_arrow_const.pass.cpp @@ -54,6 +54,25 @@ int main(int, char**) constexpr optional opt(Z{}); static_assert(opt->test() == 1, ""); } +#if TEST_STD_VER >= 26 + { + X x{}; + const std::optional opt(x); + ASSERT_SAME_TYPE(decltype(opt.operator->()), X*); + ASSERT_NOEXCEPT(opt.operator->()); + } + { + X x{}; + const std::optional opt(x); + ASSERT_SAME_TYPE(decltype(opt.operator->()), const X*); + ASSERT_NOEXCEPT(opt.operator->()); + } + { + static constexpr Z z{}; + constexpr optional opt(z); + static_assert(opt->test() == 1, ""); + } +#endif return 0; } diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/value.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/value.pass.cpp index 781784c6806a4..22b74f5512d53 100644 --- a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/value.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/value.pass.cpp @@ -56,6 +56,14 @@ int main(int, char**) opt.emplace(); assert(opt.value().test() == 4); } +#if TEST_STD_VER >= 26 + { + X x; + optional opt{x}; + ASSERT_NOT_NOEXCEPT(opt.value()); + ASSERT_SAME_TYPE(decltype(opt.value()), X&); + } +#endif #ifndef TEST_HAS_NO_EXCEPTIONS { optional opt; diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/value_or.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/value_or.pass.cpp index 8c063ae1a799c..66890ff9c9b91 100644 --- a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/value_or.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/value_or.pass.cpp @@ -80,6 +80,14 @@ constexpr int test() assert((std::move(opt).value_or({2, 3}) == Z{2, 3})); assert(!opt); } +#if TEST_STD_VER >= 26 + { + int y = 2; + optional opt; + assert(std::move(opt).value_or(y) == 2); + assert(!opt); + } +#endif return 0; } diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/value_or_const.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/value_or_const.pass.cpp index ec42890a3b995..133e993aadeb0 100644 --- a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/value_or_const.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/value_or_const.pass.cpp @@ -79,6 +79,12 @@ int main(int, char**) const optional opt; assert(opt.value_or({Y(3)}) == 4); } - +#if TEST_STD_VER >= 26 + { + X y{3}; + const optional opt; + assert(opt.value_or(y) == 3); + } +#endif return 0; } diff --git a/libcxx/test/std/utilities/optional/optional.object/optional_requires_destructible_object.verify.cpp b/libcxx/test/std/utilities/optional/optional.object/optional_requires_destructible_object.verify.cpp index a96c3c648f939..d226695d3109f 100644 --- a/libcxx/test/std/utilities/optional/optional.object/optional_requires_destructible_object.verify.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional_requires_destructible_object.verify.cpp @@ -13,6 +13,8 @@ #include +#include "test_macros.h" + using std::optional; struct X @@ -25,9 +27,14 @@ int main(int, char**) { using std::optional; { - // expected-error-re@optional:* 2 {{static assertion failed{{.*}}instantiation of optional with a reference type is ill-formed}} - optional opt1; - optional opt2; +#if TEST_STD_VER >= 26 + // expected-error-re@optional:* {{static assertion failed{{.*}}instantiation of optional with an rvalue reference type is ill-formed}} + optional opt2; +#else + // expected-error-re@optional:* 2 {{static assertion failed{{.*}}instantiation of optional with a reference type is ill-formed}} + optional opt1; + optional opt2; +#endif } { // expected-error-re@optional:* {{static assertion failed{{.*}}instantiation of optional with a non-destructible type is ill-formed}} From df2e9e7646a71ccc68ac3de7d7b797e42d652fcc Mon Sep 17 00:00:00 2001 From: William Tran-Viet Date: Sun, 24 Aug 2025 23:29:06 -0400 Subject: [PATCH 04/39] formatting --- libcxx/include/optional | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/libcxx/include/optional b/libcxx/include/optional index 2c94a4a34da8c..d0db97f0660d9 100644 --- a/libcxx/include/optional +++ b/libcxx/include/optional @@ -628,26 +628,27 @@ public: // [optional.iterators], iterator support _LIBCPP_HIDE_FROM_ABI constexpr iterator begin() noexcept { - auto& derived_self = static_cast&>(*this); + auto& __derived_self = static_cast&>(*this); # ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL return std::__make_bounded_iter( - std::__wrap_iter<__pointer>(std::addressof(derived_self.__get())), - std::__wrap_iter<__pointer>(std::addressof(derived_self.__get())), - std::__wrap_iter<__pointer>(std::addressof(derived_self.__get()) + (derived_self.has_value() ? 1 : 0))); + std::__wrap_iter<__pointer>(std::addressof(__derived_self.__get())), + std::__wrap_iter<__pointer>(std::addressof(__derived_self.__get())), + std::__wrap_iter<__pointer>(std::addressof(__derived_self.__get()) + (__derived_self.has_value() ? 1 : 0))); # else - return iterator(std::addressof(derived_self.__get())); + return iterator(std::addressof(__derived_self.__get())); # endif } _LIBCPP_HIDE_FROM_ABI constexpr const_iterator begin() const noexcept { - auto& derived_self = static_cast&>(*this); + auto& __derived_self = static_cast&>(*this); # ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL return std::__make_bounded_iter( - std::__wrap_iter<__const_pointer>(std::addressof(derived_self.__get())), - std::__wrap_iter<__const_pointer>(std::addressof(derived_self.__get())), - std::__wrap_iter<__const_pointer>(std::addressof(derived_self.__get()) + (derived_self.has_value() ? 1 : 0))); + std::__wrap_iter<__const_pointer>(std::addressof(__derived_self.__get())), + std::__wrap_iter<__const_pointer>(std::addressof(__derived_self.__get())), + std::__wrap_iter<__const_pointer>( + std::addressof(__derived_self.__get()) + (__derived_self.has_value() ? 1 : 0))); # else - return const_iterator(std::addressof(derived_self.__get())); + return const_iterator(std::addressof(__derived_self.__get())); # endif } From 322079d556a381e11e7832836b44dc55e80307ec Mon Sep 17 00:00:00 2001 From: William Tran-Viet Date: Mon, 25 Aug 2025 00:03:37 -0400 Subject: [PATCH 05/39] Forgot to gate a test --- .../optional.object/optional.object.dtor/dtor.pass.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.dtor/dtor.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.dtor/dtor.pass.cpp index cb9f757c2cc9b..f9744ae0cbd84 100644 --- a/libcxx/test/std/utilities/optional/optional.object/optional.object.dtor/dtor.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.dtor/dtor.pass.cpp @@ -11,9 +11,9 @@ // ~optional(); +#include #include #include -#include #include "test_macros.h" @@ -64,12 +64,12 @@ int main(int, char**) } assert(X::dtor_called == true); } - +#if TEST_STD_VER >= 26 { typedef int& T; static_assert(std::is_trivially_destructible::value, ""); static_assert(std::is_trivially_destructible>::value, ""); } - +#endif return 0; } From 2a014d8b8f8a3a2578b476ed08d032dbf2dfeafd Mon Sep 17 00:00:00 2001 From: William Tran-Viet Date: Mon, 25 Aug 2025 00:32:46 -0400 Subject: [PATCH 06/39] Update generated files --- libcxx/include/version | 5 ++++- .../support.limits.general/optional.version.compile.pass.cpp | 4 ++-- .../support.limits.general/version.version.compile.pass.cpp | 4 ++-- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/libcxx/include/version b/libcxx/include/version index 0fef1bb87cf60..16a5fde02b49d 100644 --- a/libcxx/include/version +++ b/libcxx/include/version @@ -185,7 +185,8 @@ __cpp_lib_nonmember_container_access 201411L __cpp_lib_not_fn 202306L 201603L // C++17 __cpp_lib_null_iterators 201304L -__cpp_lib_optional 202110L +__cpp_lib_optional 202506L + 202110L // C++23 202106L // C++20 201606L // C++17 __cpp_lib_optional_range_support 202406L @@ -588,6 +589,8 @@ __cpp_lib_void_t 201411L # define __cpp_lib_mdspan 202406L # undef __cpp_lib_not_fn # define __cpp_lib_not_fn 202306L +# undef __cpp_lib_optional +# define __cpp_lib_optional 202506L # define __cpp_lib_optional_range_support 202406L # undef __cpp_lib_out_ptr # define __cpp_lib_out_ptr 202311L diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/optional.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/optional.version.compile.pass.cpp index aca6290f5a4bf..c4e652979a4e6 100644 --- a/libcxx/test/std/language.support/support.limits/support.limits.general/optional.version.compile.pass.cpp +++ b/libcxx/test/std/language.support/support.limits/support.limits.general/optional.version.compile.pass.cpp @@ -142,8 +142,8 @@ # ifndef __cpp_lib_optional # error "__cpp_lib_optional should be defined in c++26" # endif -# if __cpp_lib_optional != 202110L -# error "__cpp_lib_optional should have the value 202110L in c++26" +# if __cpp_lib_optional != 202506L +# error "__cpp_lib_optional should have the value 202506L in c++26" # endif # ifndef __cpp_lib_optional_range_support diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp index 05af1fb0cf14b..0f8270c9bc486 100644 --- a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp +++ b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp @@ -7455,8 +7455,8 @@ # ifndef __cpp_lib_optional # error "__cpp_lib_optional should be defined in c++26" # endif -# if __cpp_lib_optional != 202110L -# error "__cpp_lib_optional should have the value 202110L in c++26" +# if __cpp_lib_optional != 202506L +# error "__cpp_lib_optional should have the value 202506L in c++26" # endif # ifndef __cpp_lib_optional_range_support From 294cb8e95cb1f58c3a202d4fa17b13371fd22185 Mon Sep 17 00:00:00 2001 From: William Tran-Viet Date: Mon, 25 Aug 2025 00:41:20 -0400 Subject: [PATCH 07/39] Formatting --- .../iterator.compile.pass.cpp | 4 +-- .../value_or.compile.pass.cpp | 4 +-- .../optional.monadic/and_then.pass.cpp | 1 - .../optional.monadic/transform.pass.cpp | 2 +- .../assign_value.pass.cpp | 3 +- .../optional.object.assign/emplace.pass.cpp | 30 +++++++++---------- .../optional.object.ctor/ctor.verify.cpp | 24 ++++++++------- .../optional.object.ctor/ref_t.pass.cpp | 4 +-- .../optional.object.dtor/dtor.pass.cpp | 10 +++---- .../optional.object.mod/reset.pass.cpp | 4 +-- .../dereference_const.pass.cpp | 22 +++++++------- .../optional.object.observe/op_arrow.pass.cpp | 7 ++--- .../value_or_const.pass.cpp | 10 +++---- 13 files changed, 63 insertions(+), 62 deletions(-) diff --git a/libcxx/test/libcxx/utilities/optional/optional.iterator/iterator.compile.pass.cpp b/libcxx/test/libcxx/utilities/optional/optional.iterator/iterator.compile.pass.cpp index 472049a91a8d1..b604579e43557 100644 --- a/libcxx/test/libcxx/utilities/optional/optional.iterator/iterator.compile.pass.cpp +++ b/libcxx/test/libcxx/utilities/optional/optional.iterator/iterator.compile.pass.cpp @@ -25,5 +25,5 @@ static_assert(has_iterator_aliases>); static_assert(has_iterator_aliases>); static_assert(has_iterator_aliases>); static_assert(has_iterator_aliases>); -static_assert(!has_iterator_aliases>); -static_assert(!has_iterator_aliases>); +static_assert(!has_iterator_aliases>); +static_assert(!has_iterator_aliases>); diff --git a/libcxx/test/libcxx/utilities/optional/optional.object/optional.object.observe/value_or.compile.pass.cpp b/libcxx/test/libcxx/utilities/optional/optional.object/optional.object.observe/value_or.compile.pass.cpp index 298e2f6f55d4d..7c2c9bc2360c5 100644 --- a/libcxx/test/libcxx/utilities/optional/optional.object/optional.object.observe/value_or.compile.pass.cpp +++ b/libcxx/test/libcxx/utilities/optional/optional.object/optional.object.observe/value_or.compile.pass.cpp @@ -23,5 +23,5 @@ concept has_value_or = requires(Opt opt, T&& t) { static_assert(has_value_or, int>); static_assert(has_value_or, int&>); static_assert(has_value_or, const int&>); -static_assert(!has_value_or&&, int(&)[1]>); -static_assert(!has_value_or&&, int(&)()>); +static_assert(!has_value_or&&, int (&)[1]>); +static_assert(!has_value_or&&, int (&)()>); diff --git a/libcxx/test/std/utilities/optional/optional.monadic/and_then.pass.cpp b/libcxx/test/std/utilities/optional/optional.monadic/and_then.pass.cpp index 3d945e437151e..310ae1609faed 100644 --- a/libcxx/test/std/utilities/optional/optional.monadic/and_then.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.monadic/and_then.pass.cpp @@ -304,7 +304,6 @@ constexpr bool test_ref() { ASSERT_SAME_TYPE(decltype(i.and_then(l)), std::optional); } } - // Test && overload { diff --git a/libcxx/test/std/utilities/optional/optional.monadic/transform.pass.cpp b/libcxx/test/std/utilities/optional/optional.monadic/transform.pass.cpp index 0d14ceb3bda8c..2faf58f798553 100644 --- a/libcxx/test/std/utilities/optional/optional.monadic/transform.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.monadic/transform.pass.cpp @@ -238,7 +238,7 @@ constexpr bool test_ref() { } } // const& overload - { + { // Without & qualifier on F's operator() { int i = 42; diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.assign/assign_value.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.assign/assign_value.pass.cpp index d633f91dc6fa5..dda461e89e48e 100644 --- a/libcxx/test/std/utilities/optional/optional.object/optional.object.assign/assign_value.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.assign/assign_value.pass.cpp @@ -279,8 +279,7 @@ constexpr void test_with_ref() { assert(static_cast(opt) == false); } } -#endif - +#endif int main(int, char**) { diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.assign/emplace.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.assign/emplace.pass.cpp index 1c921f74946ec..69709a07616a4 100644 --- a/libcxx/test/std/utilities/optional/optional.object/optional.object.assign/emplace.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.assign/emplace.pass.cpp @@ -224,20 +224,20 @@ TEST_CONSTEXPR_CXX20 bool test_empty_emplace() { #if TEST_STD_VER >= 26 template _Val> constexpr bool test_ref() { - using Opt = std::optional; - T t{_Val}; - { - Opt opt; - auto & v = opt.emplace(t); - static_assert(std::is_same_v, ""); - assert(static_cast(opt) == true); - assert(*opt == t); - assert(&v == &*opt); - assert(&t == &*opt); - } - return true; + using Opt = std::optional; + T t{_Val}; + { + Opt opt; + auto& v = opt.emplace(t); + static_assert(std::is_same_v, ""); + assert(static_cast(opt) == true); + assert(*opt == t); + assert(&v == &*opt); + assert(&t == &*opt); + } + return true; } -#endif +#endif int main(int, char**) { @@ -314,6 +314,6 @@ int main(int, char**) static_assert(test_ref()); assert((test_ref())); assert((test_ref())); -#endif - return 0; +#endif + return 0; } diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/ctor.verify.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/ctor.verify.cpp index a484a26f45c0d..ccbd24f6d8808 100644 --- a/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/ctor.verify.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/ctor.verify.cpp @@ -23,17 +23,21 @@ struct NonDestructible { ~NonDestructible() = delete; }; int main(int, char**) { - { + { #if TEST_STD_VER >= 26 - std::optional opt2; // expected-error-re@optional:* {{static assertion failed{{.*}}instantiation of optional with an rvalue reference type is ill-formed}} -#else - std::optional o1; // expected-error-re@optional:* {{static assertion failed{{.*}}instantiation of optional with a reference type is ill-formed}} -#endif - std::optional o2; // expected-error-re@optional:* {{static assertion failed{{.*}}instantiation of optional with a non-destructible type is ill-formed}} - std::optional o3; // expected-error-re@optional:* {{static assertion failed{{.*}}instantiation of optional with an array type is ill-formed}} - } - - { + std::optional + opt2; // expected-error-re@optional:* {{static assertion failed{{.*}}instantiation of optional with an rvalue reference type is ill-formed}} +#else + std::optional + o1; // expected-error-re@optional:* {{static assertion failed{{.*}}instantiation of optional with a reference type is ill-formed}} +#endif + std::optional + o2; // expected-error-re@optional:* {{static assertion failed{{.*}}instantiation of optional with a non-destructible type is ill-formed}} + std::optional + o3; // expected-error-re@optional:* {{static assertion failed{{.*}}instantiation of optional with an array type is ill-formed}} + } + + { std::optional< std::in_place_t> o1; // expected-error-re@optional:* {{static assertion failed{{.*}}instantiation of optional with in_place_t is ill-formed}} std::optional o2; // expected-error-re@optional:* {{static assertion failed{{.*}}instantiation of optional with in_place_t is ill-formed}} std::optional< volatile std::in_place_t> o3; // expected-error-re@optional:* {{static assertion failed{{.*}}instantiation of optional with in_place_t is ill-formed}} diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/ref_t.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/ref_t.pass.cpp index e3d225a8201bc..3ef41f6784615 100644 --- a/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/ref_t.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/ref_t.pass.cpp @@ -25,8 +25,8 @@ constexpr bool test() { assert(&(*opt) == &item); } { - assert(*std::as_const(opt) == item); - assert(&(*std::as_const(opt)) == &item); + assert(*std::as_const(opt) == item); + assert(&(*std::as_const(opt)) == &item); } return true; diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.dtor/dtor.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.dtor/dtor.pass.cpp index f9744ae0cbd84..a14e2dc21649a 100644 --- a/libcxx/test/std/utilities/optional/optional.object/optional.object.dtor/dtor.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.dtor/dtor.pass.cpp @@ -66,10 +66,10 @@ int main(int, char**) } #if TEST_STD_VER >= 26 { - typedef int& T; - static_assert(std::is_trivially_destructible::value, ""); - static_assert(std::is_trivially_destructible>::value, ""); + typedef int& T; + static_assert(std::is_trivially_destructible::value, ""); + static_assert(std::is_trivially_destructible>::value, ""); } -#endif - return 0; +#endif + return 0; } diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.mod/reset.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.mod/reset.pass.cpp index e633e3f1d8f37..e23e481f6a05d 100644 --- a/libcxx/test/std/utilities/optional/optional.object/optional.object.mod/reset.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.mod/reset.pass.cpp @@ -78,7 +78,7 @@ int main(int, char**) assert(X::dtor_called == false); assert(static_cast(opt) == false); } -#endif +#endif - return 0; + return 0; } diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/dereference_const.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/dereference_const.pass.cpp index cbeb0c9e288e1..d2ebafbc3adbb 100644 --- a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/dereference_const.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/dereference_const.pass.cpp @@ -45,21 +45,21 @@ int main(int, char**) } #if TEST_STD_VER >= 26 { - X x{}; - const optional opt{x}; - ASSERT_SAME_TYPE(decltype(*opt), X&); - ASSERT_NOEXCEPT(*opt); + X x{}; + const optional opt{x}; + ASSERT_SAME_TYPE(decltype(*opt), X&); + ASSERT_NOEXCEPT(*opt); } { - X x{}; - const optional opt{x}; - ASSERT_SAME_TYPE(decltype(*opt), const X&); - ASSERT_NOEXCEPT(*opt); + X x{}; + const optional opt{x}; + ASSERT_SAME_TYPE(decltype(*opt), const X&); + ASSERT_NOEXCEPT(*opt); } { - static constexpr X x{}; - constexpr optional opt(x); - static_assert((*opt).test() == 3, ""); + static constexpr X x{}; + constexpr optional opt(x); + static_assert((*opt).test() == 3, ""); } #endif { diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/op_arrow.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/op_arrow.pass.cpp index ab5998feede3e..96d22743ac7fe 100644 --- a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/op_arrow.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/op_arrow.pass.cpp @@ -19,10 +19,9 @@ using std::optional; -struct X -{ - int test() noexcept {return 3;} - int test() const noexcept {return 3;} +struct X { + int test() noexcept { return 3; } + int test() const noexcept { return 3; } }; struct Y diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/value_or_const.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/value_or_const.pass.cpp index 133e993aadeb0..6bd308b405605 100644 --- a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/value_or_const.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/value_or_const.pass.cpp @@ -81,10 +81,10 @@ int main(int, char**) } #if TEST_STD_VER >= 26 { - X y{3}; - const optional opt; - assert(opt.value_or(y) == 3); + X y{3}; + const optional opt; + assert(opt.value_or(y) == 3); } -#endif - return 0; +#endif + return 0; } From 92137cb85192c04534b6839aaca52130dd3c0838 Mon Sep 17 00:00:00 2001 From: William Tran-Viet Date: Wed, 27 Aug 2025 00:00:50 -0400 Subject: [PATCH 08/39] Remove nullptr dereference in iterator --- libcxx/include/optional | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/libcxx/include/optional b/libcxx/include/optional index d0db97f0660d9..309f09a6f028a 100644 --- a/libcxx/include/optional +++ b/libcxx/include/optional @@ -629,26 +629,39 @@ public: // [optional.iterators], iterator support _LIBCPP_HIDE_FROM_ABI constexpr iterator begin() noexcept { auto& __derived_self = static_cast&>(*this); + auto __ptr = [&__derived_self]() { + if constexpr (is_lvalue_reference_v<_Tp>) { + return __derived_self.has_value() ? std::addressof(__derived_self.__get()) : nullptr; + } + return std::addressof(__derived_self.__get()); + }(); + # ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL return std::__make_bounded_iter( - std::__wrap_iter<__pointer>(std::addressof(__derived_self.__get())), - std::__wrap_iter<__pointer>(std::addressof(__derived_self.__get())), - std::__wrap_iter<__pointer>(std::addressof(__derived_self.__get()) + (__derived_self.has_value() ? 1 : 0))); + std::__wrap_iter<__pointer>(__ptr), + std::__wrap_iter<__pointer>(__ptr), + std::__wrap_iter<__pointer>(__ptr) + (__derived_self.has_value() ? 1 : 0))); # else - return iterator(std::addressof(__derived_self.__get())); + return iterator(__ptr); # endif } _LIBCPP_HIDE_FROM_ABI constexpr const_iterator begin() const noexcept { auto& __derived_self = static_cast&>(*this); + auto* __ptr = [&__derived_self]() { + if constexpr (is_lvalue_reference_v<_Tp>) { + return __derived_self.has_value() ? std::addressof(__derived_self.__get()) : nullptr; + } + return std::addressof(__derived_self.__get()); + }(); + # ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL return std::__make_bounded_iter( - std::__wrap_iter<__const_pointer>(std::addressof(__derived_self.__get())), - std::__wrap_iter<__const_pointer>(std::addressof(__derived_self.__get())), - std::__wrap_iter<__const_pointer>( - std::addressof(__derived_self.__get()) + (__derived_self.has_value() ? 1 : 0))); + std::__wrap_iter<__const_pointer>(__ptr), + std::__wrap_iter<__const_pointer>(__ptr), + std::__wrap_iter<__const_pointer>(__ptr) + (__derived_self.has_value() ? 1 : 0))); # else - return const_iterator(std::addressof(__derived_self.__get())); + return const_iterator(__ptr); # endif } From c1235517c8e528f93209c91aaaaa17ef24b7d156 Mon Sep 17 00:00:00 2001 From: William Tran-Viet Date: Wed, 27 Aug 2025 18:58:44 -0400 Subject: [PATCH 09/39] Left in an extraneous paren --- libcxx/include/optional | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libcxx/include/optional b/libcxx/include/optional index 309f09a6f028a..5020b9074017c 100644 --- a/libcxx/include/optional +++ b/libcxx/include/optional @@ -640,7 +640,7 @@ public: return std::__make_bounded_iter( std::__wrap_iter<__pointer>(__ptr), std::__wrap_iter<__pointer>(__ptr), - std::__wrap_iter<__pointer>(__ptr) + (__derived_self.has_value() ? 1 : 0))); + std::__wrap_iter<__pointer>(__ptr) + (__derived_self.has_value() ? 1 : 0)); # else return iterator(__ptr); # endif @@ -659,7 +659,7 @@ public: return std::__make_bounded_iter( std::__wrap_iter<__const_pointer>(__ptr), std::__wrap_iter<__const_pointer>(__ptr), - std::__wrap_iter<__const_pointer>(__ptr) + (__derived_self.has_value() ? 1 : 0))); + std::__wrap_iter<__const_pointer>(__ptr) + (__derived_self.has_value() ? 1 : 0)); # else return const_iterator(__ptr); # endif From 9075442067601d67593b25d577d3b16ecb5f3e25 Mon Sep 17 00:00:00 2001 From: William Tran-Viet Date: Wed, 24 Sep 2025 19:04:30 -0400 Subject: [PATCH 10/39] Python formatting --- libcxx/utils/generate_feature_test_macro_components.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py index 51bfb35afc046..a2c690d2670c5 100644 --- a/libcxx/utils/generate_feature_test_macro_components.py +++ b/libcxx/utils/generate_feature_test_macro_components.py @@ -1006,7 +1006,7 @@ def add_version_header(tc): "c++17": 201606, "c++20": 202106, # P2231R1 Missing constexpr in std::optional and std::variant "c++23": 202110, # P0798R8 Monadic operations for std::optional + LWG3621 Remove feature-test macro __cpp_lib_monadic_optional - "c++26": 202506 # P2988R12: std::optional + "c++26": 202506, # P2988R12: std::optional }, "headers": ["optional"], }, From 0ca1d51605fd17bb2e6cbdd4ff4212c6b6fe42f8 Mon Sep 17 00:00:00 2001 From: William Tran-Viet Date: Wed, 24 Sep 2025 19:08:36 -0400 Subject: [PATCH 11/39] Fix merge oops --- libcxx/include/optional | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libcxx/include/optional b/libcxx/include/optional index 5020b9074017c..7559ce691b64a 100644 --- a/libcxx/include/optional +++ b/libcxx/include/optional @@ -951,7 +951,7 @@ public: return std::move(this->__get()); } - template = remove_cv_t<_Tp>> + template > # if _LIBCPP_STD_VER >= 26 requires(!(is_lvalue_reference_v<_Tp> && is_function_v<__libcpp_remove_reference_t<_Tp>>) && !(is_lvalue_reference_v<_Tp> && is_array_v<__libcpp_remove_reference_t<_Tp>>)) @@ -962,7 +962,7 @@ public: return this->has_value() ? this->__get() : static_cast(std::forward<_Up>(__v)); } - template = remove_cv_t<_Tp>> + template > # if _LIBCPP_STD_VER >= 26 requires(!is_lvalue_reference_v<_Tp>) # endif From 4ba0c8f74cd20b66677e1ab6dcac11fec58fee05 Mon Sep 17 00:00:00 2001 From: William Tran-Viet Date: Wed, 24 Sep 2025 19:21:26 -0400 Subject: [PATCH 12/39] Formatting and include --- libcxx/include/optional | 4 ++-- .../optional.object.observe/value_or.compile.pass.cpp | 1 + .../std/utilities/optional/optional.monadic/and_then.pass.cpp | 1 - .../optional.object/optional.object.ctor/ctor.verify.cpp | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libcxx/include/optional b/libcxx/include/optional index 7559ce691b64a..d3ccf788755a8 100644 --- a/libcxx/include/optional +++ b/libcxx/include/optional @@ -629,8 +629,8 @@ public: // [optional.iterators], iterator support _LIBCPP_HIDE_FROM_ABI constexpr iterator begin() noexcept { auto& __derived_self = static_cast&>(*this); - auto __ptr = [&__derived_self]() { - if constexpr (is_lvalue_reference_v<_Tp>) { + auto __ptr = [&__derived_self]() { + if constexpr (is_lvalue_reference_v<_Tp>) { return __derived_self.has_value() ? std::addressof(__derived_self.__get()) : nullptr; } return std::addressof(__derived_self.__get()); diff --git a/libcxx/test/libcxx/utilities/optional/optional.object/optional.object.observe/value_or.compile.pass.cpp b/libcxx/test/libcxx/utilities/optional/optional.object/optional.object.observe/value_or.compile.pass.cpp index 7c2c9bc2360c5..25df0dd6c1936 100644 --- a/libcxx/test/libcxx/utilities/optional/optional.object/optional.object.observe/value_or.compile.pass.cpp +++ b/libcxx/test/libcxx/utilities/optional/optional.object/optional.object.observe/value_or.compile.pass.cpp @@ -13,6 +13,7 @@ // template T optional::value_or(U&&); +#include #include template diff --git a/libcxx/test/std/utilities/optional/optional.monadic/and_then.pass.cpp b/libcxx/test/std/utilities/optional/optional.monadic/and_then.pass.cpp index 310ae1609faed..1630fadc7e381 100644 --- a/libcxx/test/std/utilities/optional/optional.monadic/and_then.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.monadic/and_then.pass.cpp @@ -306,7 +306,6 @@ constexpr bool test_ref() { } // Test && overload { - //With & qualifier on F's operator() { int j = 42; diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/ctor.verify.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/ctor.verify.cpp index ccbd24f6d8808..c5281783d4350 100644 --- a/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/ctor.verify.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/ctor.verify.cpp @@ -42,7 +42,7 @@ int main(int, char**) std::optional o2; // expected-error-re@optional:* {{static assertion failed{{.*}}instantiation of optional with in_place_t is ill-formed}} std::optional< volatile std::in_place_t> o3; // expected-error-re@optional:* {{static assertion failed{{.*}}instantiation of optional with in_place_t is ill-formed}} std::optional o4; // expected-error-re@optional:* {{static assertion failed{{.*}}instantiation of optional with in_place_t is ill-formed}} - } + } { std::optional< std::nullopt_t> o1; // expected-error-re@optional:* {{static assertion failed{{.*}}instantiation of optional with nullopt_t is ill-formed}} From 660716c6c7bb94d6177898710824f7677a997cd6 Mon Sep 17 00:00:00 2001 From: William Tran-Viet Date: Wed, 24 Sep 2025 23:52:46 -0400 Subject: [PATCH 13/39] Gate ref tests, use std::same_as suggestion --- .../optional.monadic/and_then.pass.cpp | 33 ++++++++++------ .../optional.monadic/transform.pass.cpp | 39 ++++++++++--------- 2 files changed, 42 insertions(+), 30 deletions(-) diff --git a/libcxx/test/std/utilities/optional/optional.monadic/and_then.pass.cpp b/libcxx/test/std/utilities/optional/optional.monadic/and_then.pass.cpp index 1630fadc7e381..133eed4a606bb 100644 --- a/libcxx/test/std/utilities/optional/optional.monadic/and_then.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.monadic/and_then.pass.cpp @@ -16,6 +16,7 @@ // template constexpr auto and_then(F&&) const&&; #include +#include #include #include "test_macros.h" @@ -265,9 +266,10 @@ constexpr bool test_ref() { { int j = 42; std::optional i{j}; - assert(i.and_then(LVal{}) == 1); + std::same_as> decltype(auto) r = i.and_then(LVal{}); + + assert(r == 1); assert(i.and_then(NOLVal{}) == std::nullopt); - ASSERT_SAME_TYPE(decltype(i.and_then(LVal{})), std::optional); } //With & qualifier on F's operator() @@ -275,10 +277,11 @@ constexpr bool test_ref() { int j = 42; std::optional i{j}; RefQual l{}; - assert(i.and_then(l) == 1); NORefQual nl{}; + std::same_as> decltype(auto) r = i.and_then(l); + + assert(r == 1); assert(i.and_then(nl) == std::nullopt); - ASSERT_SAME_TYPE(decltype(i.and_then(l)), std::optional); } } @@ -288,9 +291,10 @@ constexpr bool test_ref() { { int j = 42; std::optional i{j}; - assert(i.and_then(CLVal{}) == 1); + std::same_as> decltype(auto) r = i.and_then(CLVal{}); + + assert(r == 1); assert(i.and_then(NOCLVal{}) == std::nullopt); - ASSERT_SAME_TYPE(decltype(i.and_then(CLVal{})), std::optional); } //With & qualifier on F's operator() @@ -298,10 +302,11 @@ constexpr bool test_ref() { int j = 42; const std::optional i{j}; const CRefQual l{}; - assert(i.and_then(l) == 1); const NOCRefQual nl{}; + std::same_as> decltype(auto) r = i.and_then(l); + + assert(r == 1); assert(i.and_then(nl) == std::nullopt); - ASSERT_SAME_TYPE(decltype(i.and_then(l)), std::optional); } } // Test && overload @@ -310,9 +315,10 @@ constexpr bool test_ref() { { int j = 42; std::optional i{j}; - assert(i.and_then(RVRefQual{}) == 1); + std::same_as> decltype(auto) r = i.and_then(RVRefQual{}); + + assert(r == 1); assert(i.and_then(NORVRefQual{}) == std::nullopt); - ASSERT_SAME_TYPE(decltype(i.and_then(RVRefQual{})), std::optional); } } @@ -323,10 +329,11 @@ constexpr bool test_ref() { int j = 42; const std::optional i{j}; const RVCRefQual l{}; - assert(i.and_then(std::move(l)) == 1); const NORVCRefQual nl{}; + std::same_as> decltype(auto) r = i.and_then(std::move(l)); + + assert(r == 1); assert(i.and_then(std::move(nl)) == std::nullopt); - ASSERT_SAME_TYPE(decltype(i.and_then(std::move(l))), std::optional); } } return true; @@ -336,7 +343,9 @@ constexpr bool test_ref() { int main(int, char**) { test(); static_assert(test()); +#if TEST_STD_VER >= 26 test_ref(); static_assert(test_ref()); +#endif return 0; } diff --git a/libcxx/test/std/utilities/optional/optional.monadic/transform.pass.cpp b/libcxx/test/std/utilities/optional/optional.monadic/transform.pass.cpp index 2faf58f798553..5081261bce797 100644 --- a/libcxx/test/std/utilities/optional/optional.monadic/transform.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.monadic/transform.pass.cpp @@ -17,6 +17,7 @@ #include "test_macros.h" #include +#include #include #include #include @@ -201,20 +202,21 @@ constexpr bool test() { return true; } +#if TEST_STD_VER >= 26 constexpr bool test_ref() { { std::optional opt1; - auto opt1r = opt1.transform([](int i) { return i + 2; }); + std::same_as> decltype(auto) opt1r = opt1.transform([](int i) { return i + 2; }); assert(!opt1); - ASSERT_SAME_TYPE(decltype(opt1r), std::optional); + assert(!opt1r); } { int i = 42; std::optional opt{i}; - auto o2 = opt.transform([](int j) { return j + 2; }); + std::same_as> decltype(auto) o2 = opt.transform([](int i) { return i + 2; }); + assert(*o2 == 44); - ASSERT_SAME_TYPE(decltype(o2), std::optional); } // Test & overload { @@ -222,9 +224,9 @@ constexpr bool test_ref() { { int i = 42; std::optional opt{i}; - auto o3 = opt.transform(LVal{}); + std::same_as> decltype(auto) o3 = opt.transform(LVal{}); + assert(*o3 == 1); - ASSERT_SAME_TYPE(decltype(o3), std::optional); } //With & qualifier on F's operator() @@ -232,9 +234,9 @@ constexpr bool test_ref() { int i = 42; std::optional opt{i}; RefQual l{}; - auto o3 = opt.transform(l); + std::same_as> decltype(auto) o3 = opt.transform(l); + assert(*o3 == 1); - ASSERT_SAME_TYPE(decltype(o3), std::optional); } } // const& overload @@ -243,9 +245,9 @@ constexpr bool test_ref() { { int i = 42; std::optional opt{i}; - auto o3 = std::as_const(opt).transform(CLVal{}); + std::same_as> decltype(auto) o3 = std::as_const(opt).transform(CLVal{}); + assert(*o3 == 1); - ASSERT_SAME_TYPE(decltype(o3), std::optional); } //With & qualifier on F's operator() @@ -253,9 +255,9 @@ constexpr bool test_ref() { int i = 42; const std::optional opt{i}; const CRefQual l{}; - auto o3 = opt.transform(l); + std::same_as> decltype(auto) o3 = opt.transform(l); + assert(*o3 == 1); - ASSERT_SAME_TYPE(decltype(o3), std::optional); } } @@ -265,18 +267,17 @@ constexpr bool test_ref() { { int i = 42; std::optional opt{i}; - auto o3 = std::move(opt).transform(RVal{}); + std::same_as> decltype(auto) o3 = std::move(opt).transform(RVal{}); + assert(*o3 == 1); - ASSERT_SAME_TYPE(decltype(o3), std::optional); } //With & qualifier on F's operator() { int i = 42; std::optional opt{i}; - auto o3 = std::move(opt).transform(RVRefQual{}); + std::same_as> decltype(auto) o3 = std::move(opt).transform(RVRefQual{}); assert(*o3 == 1); - ASSERT_SAME_TYPE(decltype(o3), std::optional); } } @@ -287,9 +288,8 @@ constexpr bool test_ref() { int i = 42; std::optional opt{i}; const RVCRefQual rvc{}; - auto o3 = opt.transform(std::move(rvc)); + std::same_as> decltype(auto) o3 = opt.transform(std::move(rvc)); assert(*o3 == 1); - ASSERT_SAME_TYPE(decltype(o3), std::optional); } } { @@ -299,11 +299,14 @@ constexpr bool test_ref() { } return true; } +#endif int main(int, char**) { test(); static_assert(test()); +#if TEST_STD_VER >= 26 test_ref(); static_assert(test_ref()); +#endif return 0; } From 717c5f9a3071323bf054d9590cb80d88735bcb95 Mon Sep 17 00:00:00 2001 From: William Tran-Viet Date: Thu, 25 Sep 2025 00:48:19 -0400 Subject: [PATCH 14/39] Fix GCC shadow warning --- .../std/utilities/optional/optional.monadic/transform.pass.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libcxx/test/std/utilities/optional/optional.monadic/transform.pass.cpp b/libcxx/test/std/utilities/optional/optional.monadic/transform.pass.cpp index 5081261bce797..ad2713f2ac5b8 100644 --- a/libcxx/test/std/utilities/optional/optional.monadic/transform.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.monadic/transform.pass.cpp @@ -214,7 +214,7 @@ constexpr bool test_ref() { { int i = 42; std::optional opt{i}; - std::same_as> decltype(auto) o2 = opt.transform([](int i) { return i + 2; }); + std::same_as> decltype(auto) o2 = opt.transform([](int j) { return j + 2; }); assert(*o2 == 44); } From 1612378ee101cc04d7591f4b79775d214c98870c Mon Sep 17 00:00:00 2001 From: William Tran-Viet Date: Fri, 26 Sep 2025 21:36:26 -0400 Subject: [PATCH 15/39] Add constraint to emplace, add deleted optional constructors, add tests --- libcxx/include/optional | 48 ++++++++++++++++++- .../ref_constructs_from_temporary.verify.cpp | 35 ++++++++++++++ 2 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/ref_constructs_from_temporary.verify.cpp diff --git a/libcxx/include/optional b/libcxx/include/optional index d3ccf788755a8..54f252dcce7b2 100644 --- a/libcxx/include/optional +++ b/libcxx/include/optional @@ -240,6 +240,7 @@ namespace std { # include <__type_traits/is_trivially_relocatable.h> # include <__type_traits/is_unbounded_array.h> # include <__type_traits/negation.h> +# include <__type_traits/reference_constructs_from_temporary.h> # include <__type_traits/remove_const.h> # include <__type_traits/remove_cv.h> # include <__type_traits/remove_cvref.h> @@ -265,6 +266,11 @@ namespace std { _LIBCPP_PUSH_MACROS # include <__undef_macros> +# if _LIBCPP_STD_VER >= 26 +# define _LIBCPP_OPT_REF_DELETED_CONS_REQUIRES(_TP, _UP) \ + requires(is_lvalue_reference_v<_TP> && reference_constructs_from_temporary_v<_TP, _UP>) +# endif + namespace std // purposefully not using versioning namespace { @@ -813,6 +819,41 @@ public: this->__construct_from(std::move(__v)); } + // deleted optional constructors +# if _LIBCPP_STD_VER >= 26 + template &, _Args...>, int> = 0> + _LIBCPP_OPT_REF_DELETED_CONS_REQUIRES(_Tp, _Up) + _LIBCPP_HIDE_FROM_ABI constexpr explicit optional(in_place_t, initializer_list<_Up> __il, _Args&&... __args) = delete; + + template ::template __enable_implicit<_Up>(), int> = 0> + _LIBCPP_OPT_REF_DELETED_CONS_REQUIRES(_Tp, _Up) + _LIBCPP_HIDE_FROM_ABI constexpr optional(_Up&& __v) = delete; + + template , + enable_if_t<_CheckOptionalArgsCtor<_Up>::template __enable_explicit<_Up>(), int> = 0> + _LIBCPP_OPT_REF_DELETED_CONS_REQUIRES(_Tp, _Up) + _LIBCPP_HIDE_FROM_ABI constexpr explicit optional(_Up&& __v) = delete; + + template ::template __enable_implicit<_Up>(), int> = 0> + _LIBCPP_OPT_REF_DELETED_CONS_REQUIRES(_Tp, _Up) + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 optional(const optional<_Up>& __v) = delete; + + template ::template __enable_explicit<_Up>(), int> = 0> + _LIBCPP_OPT_REF_DELETED_CONS_REQUIRES(_Tp, _Up) + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit optional(const optional<_Up>& __v) = delete; + + template ::template __enable_implicit<_Up>(), int> = 0> + _LIBCPP_OPT_REF_DELETED_CONS_REQUIRES(_Tp, _Up) + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 optional(optional<_Up>&& __v) = delete; + + template ::template __enable_explicit<_Up>(), int> = 0> + _LIBCPP_OPT_REF_DELETED_CONS_REQUIRES(_Tp, _Up) + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit optional(optional<_Up>&& __v) = delete; +# endif + # if _LIBCPP_STD_VER >= 23 template &, _Args...>, int> = 0> + enable_if_t&, _Args...> +# if _LIBCPP_STD_VER >= 26 + && !reference_constructs_from_temporary_v<_Tp&, _Up> +# endif + , + int> = 0> _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Tp& emplace(initializer_list<_Up> __il, _Args&&... __args) { reset(); this->__construct(__il, std::forward<_Args>(__args)...); diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/ref_constructs_from_temporary.verify.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/ref_constructs_from_temporary.verify.cpp new file mode 100644 index 0000000000000..08141137d353d --- /dev/null +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/ref_constructs_from_temporary.verify.cpp @@ -0,0 +1,35 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++26 + +// optional + +#include +#include + +struct X { + int i; + + X(int j) : i(j) {} +}; + +int main(int, char**) { + const std::optional _co(1); + std::optional _o(1); + + // expected-error-re@*:* 8 {{call to deleted constructor of 'std::optional<{{.*}}>'}} + std::optional o1{1}; // optional(U&&) + std::optional o2{std::optional(1)}; // optional(optional&&) + std::optional o3{_co}; // optional(const optional&) + std::optional o4{_o}; // optional(optional&) + std::optional o5{1}; // optional(U&&) + std::optional o6{std::optional(1)}; // optional(optional&&) + std::optional o7{_co}; // optional(const optional&) + std::optional o8{_o}; // optional(optional&) +} \ No newline at end of file From ea6ce10bd08ce7e44f17b722bd6f68bb9f1b7b7b Mon Sep 17 00:00:00 2001 From: William Tran-Viet Date: Sun, 28 Sep 2025 16:20:50 -0400 Subject: [PATCH 16/39] Remove __can_bind_reference() in favor of __reference_constructs_from_temporary_v --- libcxx/include/optional | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/libcxx/include/optional b/libcxx/include/optional index 54f252dcce7b2..59a4247457edf 100644 --- a/libcxx/include/optional +++ b/libcxx/include/optional @@ -424,28 +424,12 @@ struct __optional_storage_base<_Tp, true> { using __raw_type _LIBCPP_NODEBUG = remove_reference_t<_Tp>; __raw_type* __value_; - template - static _LIBCPP_HIDE_FROM_ABI constexpr bool __can_bind_reference() { - using _RawUp = __libcpp_remove_reference_t<_Up>; - using _UpPtr = _RawUp*; - using _RawTp = __libcpp_remove_reference_t<_Tp>; - using _TpPtr = _RawTp*; - using _CheckLValueArg = - integral_constant::value && is_convertible<_UpPtr, _TpPtr>::value) || - is_same<_RawUp, reference_wrapper<_RawTp>>::value || - is_same<_RawUp, reference_wrapper<__remove_const_t<_RawTp>>>::value >; - return (is_lvalue_reference<_Tp>::value && _CheckLValueArg::value) || - (is_rvalue_reference<_Tp>::value && !is_lvalue_reference<_Up>::value && - is_convertible<_UpPtr, _TpPtr>::value); - } - _LIBCPP_HIDE_FROM_ABI constexpr __optional_storage_base() noexcept : __value_(nullptr) {} template _LIBCPP_HIDE_FROM_ABI constexpr explicit __optional_storage_base(in_place_t, _UArg&& __uarg) : __value_(std::addressof(__uarg)) { - static_assert(__can_bind_reference<_UArg>(), + static_assert(!__reference_constructs_from_temporary_v<_Tp, _UArg>, "Attempted to construct a reference element in tuple from a " "possible temporary"); } @@ -461,7 +445,7 @@ struct __optional_storage_base<_Tp, true> { template _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __construct(_UArg&& __val) { _LIBCPP_ASSERT_INTERNAL(!has_value(), "__construct called for engaged __optional_storage"); - static_assert(__can_bind_reference<_UArg>(), + static_assert(!__reference_constructs_from_temporary_v<_Tp, _UArg>, "Attempted to construct a reference element in tuple from a " "possible temporary"); __value_ = std::addressof(__val); From 089cb7134b3d7e36e29c5733cba2351215ee2569 Mon Sep 17 00:00:00 2001 From: William Tran-Viet Date: Tue, 7 Oct 2025 23:18:02 -0400 Subject: [PATCH 17/39] Assignment shouldn't modify the underlying value, test for that and also enable some disabled tests --- libcxx/include/optional | 12 +- .../assign_value.pass.cpp | 21 +++ .../optional.object.ctor/move.pass.cpp | 124 +++++++++--------- 3 files changed, 94 insertions(+), 63 deletions(-) diff --git a/libcxx/include/optional b/libcxx/include/optional index 59a4247457edf..3872403589d1c 100644 --- a/libcxx/include/optional +++ b/libcxx/include/optional @@ -416,6 +416,11 @@ struct __optional_storage_base : __optional_destruct_base<_Tp> { __construct(std::forward<_That>(__opt).__get()); } } + + template + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __assign_from_val(_Up&& __val){ + this->__get() = std::forward<_Up>(__val); + } }; template @@ -469,6 +474,11 @@ struct __optional_storage_base<_Tp, true> { __construct(std::forward<_That>(__opt).__get()); } } + + template + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __assign_from_val(_Up&& __val){ + __value_ = std::addressof(__val); + } }; template ::value> @@ -864,7 +874,7 @@ public: int> = 0> _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 optional& operator=(_Up&& __v) { if (this->has_value()) - this->__get() = std::forward<_Up>(__v); + this->__assign_from_val(std::forward<_Up>(__v)); else this->__construct(std::forward<_Up>(__v)); return *this; diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.assign/assign_value.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.assign/assign_value.pass.cpp index dda461e89e48e..b65279b3a3f16 100644 --- a/libcxx/test/std/utilities/optional/optional.object/optional.object.assign/assign_value.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.assign/assign_value.pass.cpp @@ -278,6 +278,27 @@ constexpr void test_with_ref() { opt = {}; assert(static_cast(opt) == false); } + // test two objects, make sure that the optional only changes what it holds a reference to + { + T t2{_Val}; + optional opt{t}; + opt = t2; + + assert(std::addressof(*opt) != std::addressof(t)); + assert(std::addressof(*opt) == std::addressof(t2)); + } + // test that reassigning the reference for an optional doesn't affect the objet it's holding a reference to + { + int i = -1; + int j = 2; + optional opt{i}; + opt = j; + + assert(i == -1); + assert(std::addressof(*opt) != std::addressof(i)); + assert(std::addressof(*opt) == std::addressof(j)); + assert(*opt == 2); + } } #endif diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/move.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/move.pass.cpp index f856c1d41d05a..3769200be790b 100644 --- a/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/move.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/move.pass.cpp @@ -78,71 +78,71 @@ void test_ref(InitArgs&&... args) assert(&(*lhs) == &(*rhs)); } -void test_reference_extension() -{ -#if defined(_LIBCPP_VERSION) && 0 // FIXME these extensions are currently disabled. - using T = TestTypes::TestType; - T::reset(); - { - T t; - T::reset_constructors(); - test_ref(); - test_ref(t); - assert(T::alive == 1); - assert(T::constructed == 0); - assert(T::assigned == 0); - assert(T::destroyed == 0); - } - assert(T::destroyed == 1); - assert(T::alive == 0); - { - T t; - const T& ct = t; - T::reset_constructors(); - test_ref(); - test_ref(t); - test_ref(ct); - assert(T::alive == 1); - assert(T::constructed == 0); - assert(T::assigned == 0); - assert(T::destroyed == 0); - } - assert(T::alive == 0); - assert(T::destroyed == 1); - { - T t; - T::reset_constructors(); - test_ref(); - test_ref(std::move(t)); - assert(T::alive == 1); - assert(T::constructed == 0); - assert(T::assigned == 0); - assert(T::destroyed == 0); - } - assert(T::alive == 0); - assert(T::destroyed == 1); - { - T t; - const T& ct = t; - T::reset_constructors(); - test_ref(); - test_ref(std::move(t)); - test_ref(std::move(ct)); - assert(T::alive == 1); - assert(T::constructed == 0); - assert(T::assigned == 0); - assert(T::destroyed == 0); - } - assert(T::alive == 0); - assert(T::destroyed == 1); - { - static_assert(!std::is_copy_constructible>::value, ""); - static_assert(!std::is_copy_constructible>::value, ""); - } +void test_reference_extension() { +#if TEST_STD_VER >= 26 + using T = TestTypes::TestType; + T::reset(); + { + T t; + T::reset_constructors(); + test_ref(); + test_ref(t); + assert(T::alive == 1); + assert(T::constructed == 0); + assert(T::assigned == 0); + assert(T::destroyed == 0); + } + assert(T::destroyed == 1); + assert(T::alive == 0); + { + T t; + const T& ct = t; + T::reset_constructors(); + test_ref(); + test_ref(t); + test_ref(ct); + assert(T::alive == 1); + assert(T::constructed == 0); + assert(T::assigned == 0); + assert(T::destroyed == 0); + } + assert(T::alive == 0); + assert(T::destroyed == 1); +# if 0 // FIXME: optional is not allowed. + { + T t; + T::reset_constructors(); + test_ref(); + test_ref(std::move(t)); + assert(T::alive == 1); + assert(T::constructed == 0); + assert(T::assigned == 0); + assert(T::destroyed == 0); + } + assert(T::alive == 0); + assert(T::destroyed == 1); + { + T t; + const T& ct = t; + T::reset_constructors(); + test_ref(); + test_ref(std::move(t)); + test_ref(std::move(ct)); + assert(T::alive == 1); + assert(T::constructed == 0); + assert(T::assigned == 0); + assert(T::destroyed == 0); + } + assert(T::alive == 0); + assert(T::destroyed == 1); + { + static_assert(!std::is_copy_constructible>::value, ""); + static_assert(!std::is_copy_constructible>::value, ""); + } +# endif #endif } - int main(int, char**) { test(); From c6028985950baf8ae2775746e68201ccb89133b7 Mon Sep 17 00:00:00 2001 From: William Tran-Viet Date: Tue, 7 Oct 2025 23:23:44 -0400 Subject: [PATCH 18/39] Clang-Format --- libcxx/include/optional | 4 ++-- .../optional.object.assign/assign_value.pass.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libcxx/include/optional b/libcxx/include/optional index 3872403589d1c..3ccec37e2ac58 100644 --- a/libcxx/include/optional +++ b/libcxx/include/optional @@ -418,7 +418,7 @@ struct __optional_storage_base : __optional_destruct_base<_Tp> { } template - _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __assign_from_val(_Up&& __val){ + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __assign_from_val(_Up&& __val) { this->__get() = std::forward<_Up>(__val); } }; @@ -476,7 +476,7 @@ struct __optional_storage_base<_Tp, true> { } template - _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __assign_from_val(_Up&& __val){ + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __assign_from_val(_Up&& __val) { __value_ = std::addressof(__val); } }; diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.assign/assign_value.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.assign/assign_value.pass.cpp index b65279b3a3f16..ddb9ffc4bf80c 100644 --- a/libcxx/test/std/utilities/optional/optional.object/optional.object.assign/assign_value.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.assign/assign_value.pass.cpp @@ -284,8 +284,8 @@ constexpr void test_with_ref() { optional opt{t}; opt = t2; - assert(std::addressof(*opt) != std::addressof(t)); - assert(std::addressof(*opt) == std::addressof(t2)); + assert(std::addressof(*opt) != std::addressof(t)); + assert(std::addressof(*opt) == std::addressof(t2)); } // test that reassigning the reference for an optional doesn't affect the objet it's holding a reference to { From 42637ff927e7751f8772370fde63391d9d16b012 Mon Sep 17 00:00:00 2001 From: William Tran-Viet Date: Thu, 23 Oct 2025 17:10:51 -0400 Subject: [PATCH 19/39] Address comments + Add some tests for function refs and array refs --- libcxx/include/optional | 25 +++++------ .../optional.object.ctor/move.pass.cpp | 4 +- .../ref_constructs_from_temporary.verify.cpp | 2 +- .../optional.object.ctor/ref_t.pass.cpp | 41 +++++++++++++++++-- .../optional.object.dtor/dtor.pass.cpp | 18 ++++++-- .../has_value.pass.cpp | 2 +- ...al_requires_destructible_object.verify.cpp | 3 +- 7 files changed, 71 insertions(+), 24 deletions(-) diff --git a/libcxx/include/optional b/libcxx/include/optional index 3ccec37e2ac58..166f115101d1c 100644 --- a/libcxx/include/optional +++ b/libcxx/include/optional @@ -266,11 +266,6 @@ namespace std { _LIBCPP_PUSH_MACROS # include <__undef_macros> -# if _LIBCPP_STD_VER >= 26 -# define _LIBCPP_OPT_REF_DELETED_CONS_REQUIRES(_TP, _UP) \ - requires(is_lvalue_reference_v<_TP> && reference_constructs_from_temporary_v<_TP, _UP>) -# endif - namespace std // purposefully not using versioning namespace { @@ -702,6 +697,12 @@ private: static_assert(is_destructible_v, "instantiation of optional with a non-destructible type is ill-formed"); static_assert(!is_array_v, "instantiation of optional with an array type is ill-formed"); +# if _LIBCPP_STD_VER >= 26 + template + constexpr static bool __libcpp_opt_ref_ctor_deleted = + is_lvalue_reference_v<_Tp> && reference_constructs_from_temporary_v<_Tp, _Up>; +# endif + // LWG2756: conditionally explicit conversion from _Up struct _CheckOptionalArgsConstructor { template @@ -818,33 +819,33 @@ public: template &, _Args...>, int> = 0> - _LIBCPP_OPT_REF_DELETED_CONS_REQUIRES(_Tp, _Up) + requires __libcpp_opt_ref_ctor_deleted<_Up> _LIBCPP_HIDE_FROM_ABI constexpr explicit optional(in_place_t, initializer_list<_Up> __il, _Args&&... __args) = delete; template ::template __enable_implicit<_Up>(), int> = 0> - _LIBCPP_OPT_REF_DELETED_CONS_REQUIRES(_Tp, _Up) + requires __libcpp_opt_ref_ctor_deleted<_Up> _LIBCPP_HIDE_FROM_ABI constexpr optional(_Up&& __v) = delete; template , enable_if_t<_CheckOptionalArgsCtor<_Up>::template __enable_explicit<_Up>(), int> = 0> - _LIBCPP_OPT_REF_DELETED_CONS_REQUIRES(_Tp, _Up) + requires __libcpp_opt_ref_ctor_deleted<_Up> _LIBCPP_HIDE_FROM_ABI constexpr explicit optional(_Up&& __v) = delete; template ::template __enable_implicit<_Up>(), int> = 0> - _LIBCPP_OPT_REF_DELETED_CONS_REQUIRES(_Tp, _Up) + requires __libcpp_opt_ref_ctor_deleted<_Up> _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 optional(const optional<_Up>& __v) = delete; template ::template __enable_explicit<_Up>(), int> = 0> - _LIBCPP_OPT_REF_DELETED_CONS_REQUIRES(_Tp, _Up) + requires __libcpp_opt_ref_ctor_deleted<_Up> _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit optional(const optional<_Up>& __v) = delete; template ::template __enable_implicit<_Up>(), int> = 0> - _LIBCPP_OPT_REF_DELETED_CONS_REQUIRES(_Tp, _Up) + requires __libcpp_opt_ref_ctor_deleted<_Up> _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 optional(optional<_Up>&& __v) = delete; template ::template __enable_explicit<_Up>(), int> = 0> - _LIBCPP_OPT_REF_DELETED_CONS_REQUIRES(_Tp, _Up) + requires __libcpp_opt_ref_ctor_deleted<_Up> _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit optional(optional<_Up>&& __v) = delete; # endif diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/move.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/move.pass.cpp index 3769200be790b..f59fc3b82ad7f 100644 --- a/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/move.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/move.pass.cpp @@ -136,8 +136,8 @@ void test_reference_extension() { assert(T::alive == 0); assert(T::destroyed == 1); { - static_assert(!std::is_copy_constructible>::value, ""); - static_assert(!std::is_copy_constructible>::value, ""); + static_assert(!std::is_copy_constructible_v>); + static_assert(!std::is_copy_constructible_v>); } # endif #endif diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/ref_constructs_from_temporary.verify.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/ref_constructs_from_temporary.verify.cpp index 08141137d353d..01b241ffbe79b 100644 --- a/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/ref_constructs_from_temporary.verify.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/ref_constructs_from_temporary.verify.cpp @@ -32,4 +32,4 @@ int main(int, char**) { std::optional o6{std::optional(1)}; // optional(optional&&) std::optional o7{_co}; // optional(const optional&) std::optional o8{_o}; // optional(optional&) -} \ No newline at end of file +} diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/ref_t.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/ref_t.pass.cpp index 3ef41f6784615..57552743af138 100644 --- a/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/ref_t.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/ref_t.pass.cpp @@ -32,9 +32,44 @@ constexpr bool test() { return true; } -int main(int, char**) { - static_assert((test())); - static_assert((test())); +template +constexpr T foo(T val) { + return val; +} + +template +constexpr bool fn_ref_test() { + std::optional opt{foo}; + assert(opt.has_value()); + assert((*opt)(_Val) == _Val); + + return true; +} + +template +constexpr bool array_ref_test() { + T arr[5]{}; + std::optional opt{arr}; + + assert(opt.has_value()); + (*opt)[0] = _Val; + assert((*opt)[0] == _Val); + assert(arr[0] == _Val); + + return true; +} + +constexpr bool tests() { assert((test())); assert((test())); + assert((fn_ref_test())); + assert((array_ref_test())); + assert((fn_ref_test())); + assert((array_ref_test())); + return true; +} + +int main(int, char**) { + static_assert(tests()); + tests(); } diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.dtor/dtor.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.dtor/dtor.pass.cpp index a14e2dc21649a..937b5c3473654 100644 --- a/libcxx/test/std/utilities/optional/optional.object/optional.object.dtor/dtor.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.dtor/dtor.pass.cpp @@ -66,9 +66,21 @@ int main(int, char**) } #if TEST_STD_VER >= 26 { - typedef int& T; - static_assert(std::is_trivially_destructible::value, ""); - static_assert(std::is_trivially_destructible>::value, ""); + typedef X& T; + static_assert(std::is_trivially_destructible::value); + static_assert(std::is_trivially_destructible>::value); + } + X::dtor_called = false; + X x; + { + optional opt{x}; + assert(X::dtor_called == false); + } + assert(X::dtor_called == false); + + { + static_assert(std::is_trivially_destructible::value); + static_assert(std::is_trivially_destructible>::value); } #endif return 0; diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/has_value.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/has_value.pass.cpp index 038de500070fb..9873a767cfbe6 100644 --- a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/has_value.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/has_value.pass.cpp @@ -37,7 +37,7 @@ int main(int, char**) { static constexpr int i = 0; constexpr optional opt{i}; - static_assert(opt.has_value(), ""); + static_assert(opt.has_value()); } #endif diff --git a/libcxx/test/std/utilities/optional/optional.object/optional_requires_destructible_object.verify.cpp b/libcxx/test/std/utilities/optional/optional.object/optional_requires_destructible_object.verify.cpp index d226695d3109f..a956ab3a219cf 100644 --- a/libcxx/test/std/utilities/optional/optional.object/optional_requires_destructible_object.verify.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional_requires_destructible_object.verify.cpp @@ -29,12 +29,11 @@ int main(int, char**) { #if TEST_STD_VER >= 26 // expected-error-re@optional:* {{static assertion failed{{.*}}instantiation of optional with an rvalue reference type is ill-formed}} - optional opt2; #else // expected-error-re@optional:* 2 {{static assertion failed{{.*}}instantiation of optional with a reference type is ill-formed}} +#endif optional opt1; optional opt2; -#endif } { // expected-error-re@optional:* {{static assertion failed{{.*}}instantiation of optional with a non-destructible type is ill-formed}} From 3c9c51b6242eae8148c106b4b2b1cc40d3607daf Mon Sep 17 00:00:00 2001 From: William Tran-Viet Date: Thu, 23 Oct 2025 21:35:41 -0400 Subject: [PATCH 20/39] Change a few more static_asserts --- .../optional.object.observe/dereference_const.pass.cpp | 2 +- .../optional.object.observe/op_arrow_const.pass.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/dereference_const.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/dereference_const.pass.cpp index d2ebafbc3adbb..c15d4e4af74cc 100644 --- a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/dereference_const.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/dereference_const.pass.cpp @@ -59,7 +59,7 @@ int main(int, char**) { static constexpr X x{}; constexpr optional opt(x); - static_assert((*opt).test() == 3, ""); + static_assert((*opt).test() == 3); } #endif { diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/op_arrow_const.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/op_arrow_const.pass.cpp index 5154679cb6435..e9694fd6d9640 100644 --- a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/op_arrow_const.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/op_arrow_const.pass.cpp @@ -70,7 +70,7 @@ int main(int, char**) { static constexpr Z z{}; constexpr optional opt(z); - static_assert(opt->test() == 1, ""); + static_assert(opt->test() == 1); } #endif From dfb334938ba7c26d8d3bb3d1d13c4e339515d0f1 Mon Sep 17 00:00:00 2001 From: William Tran-Viet Date: Sun, 26 Oct 2025 17:23:18 -0400 Subject: [PATCH 21/39] Fix up swap, make_optional, add tests --- libcxx/include/optional | 25 +++++- .../optional.object.swap/swap.pass.cpp | 77 ++++++++++++++++++- .../optional.specalg/make_optional.pass.cpp | 4 +- .../make_optional_explicit.pass.cpp | 4 +- .../make_optional_ref.verify.cpp | 38 +++++++++ .../optional/optional.specalg/swap.pass.cpp | 76 +++++++++++++++++- 6 files changed, 215 insertions(+), 9 deletions(-) create mode 100644 libcxx/test/std/utilities/optional/optional.specalg/make_optional_ref.verify.cpp diff --git a/libcxx/include/optional b/libcxx/include/optional index 166f115101d1c..280e439eece79 100644 --- a/libcxx/include/optional +++ b/libcxx/include/optional @@ -416,6 +416,11 @@ struct __optional_storage_base : __optional_destruct_base<_Tp> { _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __assign_from_val(_Up&& __val) { this->__get() = std::forward<_Up>(__val); } + + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __swap(__optional_storage_base& __rhs) { + using std::swap; + swap(this->__get(), __rhs.__get()); + } }; template @@ -474,6 +479,11 @@ struct __optional_storage_base<_Tp, true> { _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __assign_from_val(_Up&& __val) { __value_ = std::addressof(__val); } + + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __swap(__optional_storage_base& __rhs) { + using std::swap; + swap(__value_, __rhs.__value_); + } }; template ::value> @@ -919,9 +929,8 @@ public: _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void swap(optional& __opt) noexcept(is_nothrow_move_constructible_v && is_nothrow_swappable_v) { if (this->has_value() == __opt.has_value()) { - using std::swap; if (this->has_value()) - swap(this->__get(), __opt.__get()); + this->__swap(__opt); } else { if (this->has_value()) { __opt.__construct(std::move(this->__get())); @@ -1428,18 +1437,28 @@ swap(optional<_Tp>& __x, optional<_Tp>& __y) noexcept(noexcept(__x.swap(__y))) { __x.swap(__y); } -template +template < +# if _LIBCPP_STD_VER >= 26 + bool = false, +# endif + class _Tp> _LIBCPP_HIDE_FROM_ABI constexpr optional> make_optional(_Tp&& __v) { return optional>(std::forward<_Tp>(__v)); } template _LIBCPP_HIDE_FROM_ABI constexpr optional<_Tp> make_optional(_Args&&... __args) { + #if _LIBCPP_STD_VER >= 26 + static_assert(!is_reference_v<_Tp>, "make_optional is disallowed"); + #endif return optional<_Tp>(in_place, std::forward<_Args>(__args)...); } template _LIBCPP_HIDE_FROM_ABI constexpr optional<_Tp> make_optional(initializer_list<_Up> __il, _Args&&... __args) { + #if _LIBCPP_STD_VER >= 26 + static_assert(!is_reference_v<_Tp>, "make_optional is disallowed"); + #endif return optional<_Tp>(in_place, __il, std::forward<_Args>(__args)...); } diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.swap/swap.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.swap/swap.pass.cpp index e3a2fdb8b0020..b53b9a4e3767b 100644 --- a/libcxx/test/std/utilities/optional/optional.object/optional.object.swap/swap.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.swap/swap.pass.cpp @@ -13,9 +13,10 @@ // noexcept(is_nothrow_move_constructible::value && // is_nothrow_swappable::value) +#include +#include #include #include -#include #include "test_macros.h" #include "archetypes.h" @@ -127,6 +128,74 @@ TEST_CONSTEXPR_CXX20 bool check_swap() return true; } +#if TEST_STD_VER >= 26 +template +constexpr bool check_swap_ref() { + { + optional opt1; + optional opt2; + static_assert(noexcept(opt1.swap(opt2)) == true); + assert(static_cast(opt1) == false); + assert(static_cast(opt2) == false); + opt1.swap(opt2); + assert(static_cast(opt1) == false); + assert(static_cast(opt2) == false); + } + + { + T one{1}; + optional opt1(one); + optional opt2; + static_assert(noexcept(opt1.swap(opt2)) == true); + assert(static_cast(opt1) == true); + assert(std::addressof(*opt1) == std::addressof(one)); + assert(static_cast(opt2) == false); + opt1.swap(opt2); + assert(static_cast(opt1) == false); + assert(static_cast(opt2) == true); + assert(std::addressof(*opt2) == std::addressof(one)); + } + + { + T two{2}; + optional opt1; + optional opt2(two); + static_assert(noexcept(opt1.swap(opt2)) == true); + assert(static_cast(opt1) == false); + assert(static_cast(opt2) == true); + assert(std::addressof(*opt2) == std::addressof(two)); + opt1.swap(opt2); + assert(static_cast(opt1) == true); + assert(std::addressof(*opt1) == std::addressof(two)); + assert(static_cast(opt2) == false); + } + + { + T one{1}; + T two{2}; + + optional opt1(one); + optional opt2(two); + static_assert(noexcept(opt1.swap(opt2)) == true); + assert(static_cast(opt1) == true); + assert(*opt1 == 1); + assert(std::addressof(*opt1) == std::addressof(one)); + assert(static_cast(opt2) == true); + assert(*opt2 == 2); + assert(std::addressof(*opt2) == std::addressof(two)); + opt1.swap(opt2); + assert(static_cast(opt1) == true); + assert(*opt1 == 2); + assert(std::addressof(*opt1) == std::addressof(two)); + assert(static_cast(opt2) == true); + assert(*opt2 == 1); + assert(std::addressof(*opt2) == std::addressof(one)); + } + + return true; +} +#endif + int main(int, char**) { check_swap(); @@ -135,6 +204,12 @@ int main(int, char**) static_assert(check_swap()); static_assert(check_swap()); #endif +#if TEST_STD_VER >= 26 + static_assert(check_swap_ref()); + static_assert(check_swap_ref()); + check_swap_ref(); + check_swap_ref(); +#endif { optional opt1; optional opt2; diff --git a/libcxx/test/std/utilities/optional/optional.specalg/make_optional.pass.cpp b/libcxx/test/std/utilities/optional/optional.specalg/make_optional.pass.cpp index e325a7af558eb..c27645165d20e 100644 --- a/libcxx/test/std/utilities/optional/optional.specalg/make_optional.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.specalg/make_optional.pass.cpp @@ -13,10 +13,10 @@ // template // constexpr optional> make_optional(T&& v); +#include +#include #include #include -#include -#include #include "test_macros.h" diff --git a/libcxx/test/std/utilities/optional/optional.specalg/make_optional_explicit.pass.cpp b/libcxx/test/std/utilities/optional/optional.specalg/make_optional_explicit.pass.cpp index 23f131d2fc499..6757c506c7ad6 100644 --- a/libcxx/test/std/utilities/optional/optional.specalg/make_optional_explicit.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.specalg/make_optional_explicit.pass.cpp @@ -15,10 +15,10 @@ // GCC crashes on this file, see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=120577 // XFAIL: gcc-15 +#include +#include #include #include -#include -#include #include "test_macros.h" diff --git a/libcxx/test/std/utilities/optional/optional.specalg/make_optional_ref.verify.cpp b/libcxx/test/std/utilities/optional/optional.specalg/make_optional_ref.verify.cpp new file mode 100644 index 0000000000000..6e3d719b0407c --- /dev/null +++ b/libcxx/test/std/utilities/optional/optional.specalg/make_optional_ref.verify.cpp @@ -0,0 +1,38 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++26 + +// + +// template +// constexpr optional make_optional(Args&&... args); + +#include + +struct Foo{ + int x, y; +}; + +class X { + double i_; + +public: + explicit X(int& i) : i_(i) {} +} + +int main(int, char**) { + int i = 1; + // expected-error-re@optional:* 3 {{static assertion failed{{.*}} make_optional is disallowed}} + std::make_optional(i); + std::make_optional(1); + std::make_optional(1,2); + + // FIXME: Garbage error messages that Clang produces after the static_assert is triggered + // expected-error-re@optional:* 0+ {{no matching constructor for initialization of 'optional<{{.*}}>'}} +} \ No newline at end of file diff --git a/libcxx/test/std/utilities/optional/optional.specalg/swap.pass.cpp b/libcxx/test/std/utilities/optional/optional.specalg/swap.pass.cpp index 0da3a821e7961..c757120a1c146 100644 --- a/libcxx/test/std/utilities/optional/optional.specalg/swap.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.specalg/swap.pass.cpp @@ -12,9 +12,10 @@ // template void swap(optional& x, optional& y) // noexcept(noexcept(x.swap(y))); +#include +#include #include #include -#include #include "test_macros.h" #include "archetypes.h" @@ -109,9 +110,82 @@ void test_swap_sfinae() { } } +#if TEST_STD_VER >= 26 +template +constexpr bool test_swap_ref() { + { + optional opt1; + optional opt2; + static_assert(noexcept(swap(opt1, opt2)) == true); + assert(static_cast(opt1) == false); + assert(static_cast(opt2) == false); + swap(opt1, opt2); + assert(static_cast(opt1) == false); + assert(static_cast(opt2) == false); + } + { + T one{1}; + optional opt1(one); + optional opt2; + static_assert(noexcept(swap(opt1, opt2)) == true); + assert(static_cast(opt1) == true); + assert(*opt1 == 1); + assert(std::addressof(*opt1) == std::addressof(one)); + assert(static_cast(opt2) == false); + swap(opt1, opt2); + assert(static_cast(opt1) == false); + assert(static_cast(opt2) == true); + assert(*opt2 == 1); + assert(std::addressof(*opt2) == std::addressof(one)); + } + { + T two{2}; + optional opt1; + optional opt2(two); + static_assert(noexcept(swap(opt1, opt2)) == true); + assert(static_cast(opt1) == false); + assert(static_cast(opt2) == true); + assert(*opt2 == 2); + assert(std::addressof(*opt2) == std::addressof(two)); + swap(opt1, opt2); + assert(static_cast(opt1) == true); + assert(*opt1 == 2); + assert(std::addressof(*opt1) == std::addressof(two)); + assert(static_cast(opt2) == false); + } + { + T one{1}; + T two{2}; + optional opt1(one); + optional opt2(two); + static_assert(noexcept(swap(opt1, opt2)) == true); + assert(static_cast(opt1) == true); + assert(*opt1 == 1); + assert(std::addressof(*opt1) == std::addressof(one)); + assert(static_cast(opt2) == true); + assert(*opt2 == 2); + assert(std::addressof(*opt2) == std::addressof(two)); + swap(opt1, opt2); + assert(static_cast(opt1) == true); + assert(*opt1 == 2); + assert(std::addressof(*opt1) == std::addressof(two)); + assert(static_cast(opt2) == true); + assert(*opt2 == 1); + assert(std::addressof(*opt2) == std::addressof(one)); + } + return true; +} +#endif + int main(int, char**) { test_swap_sfinae(); +#if TEST_STD_VER >= 26 + static_assert(test_swap_ref()); + static_assert(test_swap_ref()); + test_swap_ref(); + test_swap_ref(); +#endif { optional opt1; optional opt2; From bc6831034f649182474121c701ba0d4f19f63716 Mon Sep 17 00:00:00 2001 From: William Tran-Viet Date: Sun, 26 Oct 2025 17:24:40 -0400 Subject: [PATCH 22/39] Address nits --- .../optional.object.assign/emplace.pass.cpp | 2 +- .../optional.object/optional.object.dtor/dtor.pass.cpp | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.assign/emplace.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.assign/emplace.pass.cpp index 69709a07616a4..629e315add4d9 100644 --- a/libcxx/test/std/utilities/optional/optional.object/optional.object.assign/emplace.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.assign/emplace.pass.cpp @@ -229,7 +229,7 @@ constexpr bool test_ref() { { Opt opt; auto& v = opt.emplace(t); - static_assert(std::is_same_v, ""); + static_assert(std::is_same_v); assert(static_cast(opt) == true); assert(*opt == t); assert(&v == &*opt); diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.dtor/dtor.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.dtor/dtor.pass.cpp index 937b5c3473654..1202879036f56 100644 --- a/libcxx/test/std/utilities/optional/optional.object/optional.object.dtor/dtor.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.dtor/dtor.pass.cpp @@ -67,8 +67,8 @@ int main(int, char**) #if TEST_STD_VER >= 26 { typedef X& T; - static_assert(std::is_trivially_destructible::value); - static_assert(std::is_trivially_destructible>::value); + static_assert(std::is_trivially_destructible_v); + static_assert(std::is_trivially_destructible_v>); } X::dtor_called = false; X x; @@ -79,8 +79,8 @@ int main(int, char**) assert(X::dtor_called == false); { - static_assert(std::is_trivially_destructible::value); - static_assert(std::is_trivially_destructible>::value); + static_assert(std::is_trivially_destructible_v); + static_assert(std::is_trivially_destructible_v>); } #endif return 0; From 4f8743805cf135f7141775381608dacaabbdd43d Mon Sep 17 00:00:00 2001 From: William Tran-Viet Date: Sun, 26 Oct 2025 17:52:11 -0400 Subject: [PATCH 23/39] Formatting --- libcxx/include/optional | 12 ++++++------ .../optional.specalg/make_optional_ref.verify.cpp | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/libcxx/include/optional b/libcxx/include/optional index 280e439eece79..54330d054a7ea 100644 --- a/libcxx/include/optional +++ b/libcxx/include/optional @@ -1448,17 +1448,17 @@ _LIBCPP_HIDE_FROM_ABI constexpr optional> make_optional(_Tp&& __v) template _LIBCPP_HIDE_FROM_ABI constexpr optional<_Tp> make_optional(_Args&&... __args) { - #if _LIBCPP_STD_VER >= 26 - static_assert(!is_reference_v<_Tp>, "make_optional is disallowed"); - #endif +# if _LIBCPP_STD_VER >= 26 + static_assert(!is_reference_v<_Tp>, "make_optional is disallowed"); +# endif return optional<_Tp>(in_place, std::forward<_Args>(__args)...); } template _LIBCPP_HIDE_FROM_ABI constexpr optional<_Tp> make_optional(initializer_list<_Up> __il, _Args&&... __args) { - #if _LIBCPP_STD_VER >= 26 - static_assert(!is_reference_v<_Tp>, "make_optional is disallowed"); - #endif +# if _LIBCPP_STD_VER >= 26 + static_assert(!is_reference_v<_Tp>, "make_optional is disallowed"); +# endif return optional<_Tp>(in_place, __il, std::forward<_Args>(__args)...); } diff --git a/libcxx/test/std/utilities/optional/optional.specalg/make_optional_ref.verify.cpp b/libcxx/test/std/utilities/optional/optional.specalg/make_optional_ref.verify.cpp index 6e3d719b0407c..b183038020921 100644 --- a/libcxx/test/std/utilities/optional/optional.specalg/make_optional_ref.verify.cpp +++ b/libcxx/test/std/utilities/optional/optional.specalg/make_optional_ref.verify.cpp @@ -13,9 +13,9 @@ // template // constexpr optional make_optional(Args&&... args); -#include +#include -struct Foo{ +struct Foo { int x, y; }; @@ -31,7 +31,7 @@ int main(int, char**) { // expected-error-re@optional:* 3 {{static assertion failed{{.*}} make_optional is disallowed}} std::make_optional(i); std::make_optional(1); - std::make_optional(1,2); + std::make_optional(1, 2); // FIXME: Garbage error messages that Clang produces after the static_assert is triggered // expected-error-re@optional:* 0+ {{no matching constructor for initialization of 'optional<{{.*}}>'}} From e586acd20255d4fdca87ce369dedfaa341b037ff Mon Sep 17 00:00:00 2001 From: William Tran-Viet Date: Sun, 26 Oct 2025 23:12:16 -0400 Subject: [PATCH 24/39] Formatting x2 --- .../optional/optional.object/optional.object.swap/swap.pass.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.swap/swap.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.swap/swap.pass.cpp index b53b9a4e3767b..a82ca615e0c8c 100644 --- a/libcxx/test/std/utilities/optional/optional.object/optional.object.swap/swap.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.swap/swap.pass.cpp @@ -209,7 +209,7 @@ int main(int, char**) static_assert(check_swap_ref()); check_swap_ref(); check_swap_ref(); -#endif +#endif { optional opt1; optional opt2; From 18c1b96ccab8a44d136a3206cdccfed7e5f06d6f Mon Sep 17 00:00:00 2001 From: William Tran-Viet Date: Sun, 26 Oct 2025 23:27:37 -0400 Subject: [PATCH 25/39] Fix typo --- .../optional.specalg/make_optional_ref.verify.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/libcxx/test/std/utilities/optional/optional.specalg/make_optional_ref.verify.cpp b/libcxx/test/std/utilities/optional/optional.specalg/make_optional_ref.verify.cpp index b183038020921..9828c5c95c9f1 100644 --- a/libcxx/test/std/utilities/optional/optional.specalg/make_optional_ref.verify.cpp +++ b/libcxx/test/std/utilities/optional/optional.specalg/make_optional_ref.verify.cpp @@ -19,20 +19,21 @@ struct Foo { int x, y; }; -class X { +struct X { double i_; public: explicit X(int& i) : i_(i) {} -} +}; int main(int, char**) { int i = 1; - // expected-error-re@optional:* 3 {{static assertion failed{{.*}} make_optional is disallowed}} + // expected-error-re@optional:* 4 {{static assertion failed{{.*}} make_optional is disallowed}} + std::make_optional(i); std::make_optional(i); std::make_optional(1); std::make_optional(1, 2); - // FIXME: Garbage error messages that Clang produces after the static_assert is triggered + // FIXME: Garbage error messages that Clang produces after the static_assert is reported // expected-error-re@optional:* 0+ {{no matching constructor for initialization of 'optional<{{.*}}>'}} } \ No newline at end of file From 1add5c55c42a2ebcb1a76d33d1164e8e02444b2a Mon Sep 17 00:00:00 2001 From: William Tran-Viet Date: Mon, 27 Oct 2025 18:15:20 -0400 Subject: [PATCH 26/39] Allow make_optional for T&, add test --- libcxx/include/optional | 12 +++--- .../make_optional_explicit.pass.cpp | 25 +++++++++++- .../make_optional_ref.verify.cpp | 39 ------------------- 3 files changed, 29 insertions(+), 47 deletions(-) delete mode 100644 libcxx/test/std/utilities/optional/optional.specalg/make_optional_ref.verify.cpp diff --git a/libcxx/include/optional b/libcxx/include/optional index 54330d054a7ea..7315025f314f7 100644 --- a/libcxx/include/optional +++ b/libcxx/include/optional @@ -1437,9 +1437,13 @@ swap(optional<_Tp>& __x, optional<_Tp>& __y) noexcept(noexcept(__x.swap(__y))) { __x.swap(__y); } +struct __make_optional_barrier_tag { + explicit __make_optional_barrier_tag() = default; +}; + template < # if _LIBCPP_STD_VER >= 26 - bool = false, + __make_optional_barrier_tag = __make_optional_barrier_tag{}, # endif class _Tp> _LIBCPP_HIDE_FROM_ABI constexpr optional> make_optional(_Tp&& __v) { @@ -1448,17 +1452,11 @@ _LIBCPP_HIDE_FROM_ABI constexpr optional> make_optional(_Tp&& __v) template _LIBCPP_HIDE_FROM_ABI constexpr optional<_Tp> make_optional(_Args&&... __args) { -# if _LIBCPP_STD_VER >= 26 - static_assert(!is_reference_v<_Tp>, "make_optional is disallowed"); -# endif return optional<_Tp>(in_place, std::forward<_Args>(__args)...); } template _LIBCPP_HIDE_FROM_ABI constexpr optional<_Tp> make_optional(initializer_list<_Up> __il, _Args&&... __args) { -# if _LIBCPP_STD_VER >= 26 - static_assert(!is_reference_v<_Tp>, "make_optional is disallowed"); -# endif return optional<_Tp>(in_place, __il, std::forward<_Args>(__args)...); } diff --git a/libcxx/test/std/utilities/optional/optional.specalg/make_optional_explicit.pass.cpp b/libcxx/test/std/utilities/optional/optional.specalg/make_optional_explicit.pass.cpp index 6757c506c7ad6..5dd1d6f0b3380 100644 --- a/libcxx/test/std/utilities/optional/optional.specalg/make_optional_explicit.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.specalg/make_optional_explicit.pass.cpp @@ -19,9 +19,26 @@ #include #include #include +#include #include "test_macros.h" +template +constexpr bool test_ref() { + T i{0}; + auto opt = std::make_optional(i); + +#if TEST_STD_VER < 26 + assert((std::is_same_v>)); +#else + assert((std::is_same_v>)); +#endif + + assert(*opt == 0); + + return true; +} + int main(int, char**) { { @@ -43,6 +60,12 @@ int main(int, char**) auto opt = std::make_optional(4u, 'X'); assert(*opt == "XXXX"); } + using namespace std::string_view_literals; + + static_assert(test_ref()); + assert((test_ref())); + static_assert(test_ref()); + assert((test_ref())); - return 0; + return 0; } diff --git a/libcxx/test/std/utilities/optional/optional.specalg/make_optional_ref.verify.cpp b/libcxx/test/std/utilities/optional/optional.specalg/make_optional_ref.verify.cpp deleted file mode 100644 index 9828c5c95c9f1..0000000000000 --- a/libcxx/test/std/utilities/optional/optional.specalg/make_optional_ref.verify.cpp +++ /dev/null @@ -1,39 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -// REQUIRES: std-at-least-c++26 - -// - -// template -// constexpr optional make_optional(Args&&... args); - -#include - -struct Foo { - int x, y; -}; - -struct X { - double i_; - -public: - explicit X(int& i) : i_(i) {} -}; - -int main(int, char**) { - int i = 1; - // expected-error-re@optional:* 4 {{static assertion failed{{.*}} make_optional is disallowed}} - std::make_optional(i); - std::make_optional(i); - std::make_optional(1); - std::make_optional(1, 2); - - // FIXME: Garbage error messages that Clang produces after the static_assert is reported - // expected-error-re@optional:* 0+ {{no matching constructor for initialization of 'optional<{{.*}}>'}} -} \ No newline at end of file From 85db654f7c9e69379211533185d3929217d99b64 Mon Sep 17 00:00:00 2001 From: William Tran-Viet Date: Mon, 27 Oct 2025 18:42:07 -0400 Subject: [PATCH 27/39] Formatting --- libcxx/include/optional | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libcxx/include/optional b/libcxx/include/optional index 7315025f314f7..17bc0b176027a 100644 --- a/libcxx/include/optional +++ b/libcxx/include/optional @@ -1438,7 +1438,7 @@ swap(optional<_Tp>& __x, optional<_Tp>& __y) noexcept(noexcept(__x.swap(__y))) { } struct __make_optional_barrier_tag { - explicit __make_optional_barrier_tag() = default; + explicit __make_optional_barrier_tag() = default; }; template < From 65e92c481d1aabcced2873e40035fdbfff875419 Mon Sep 17 00:00:00 2001 From: William Tran-Viet Date: Fri, 31 Oct 2025 16:35:01 -0400 Subject: [PATCH 28/39] Use std::swap, mark T& assignment and __swap noexcept --- libcxx/include/optional | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/libcxx/include/optional b/libcxx/include/optional index 17bc0b176027a..6953c858d3160 100644 --- a/libcxx/include/optional +++ b/libcxx/include/optional @@ -476,13 +476,12 @@ struct __optional_storage_base<_Tp, true> { } template - _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __assign_from_val(_Up&& __val) { + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __assign_from_val(_Up&& __val) noexcept { __value_ = std::addressof(__val); } - _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __swap(__optional_storage_base& __rhs) { - using std::swap; - swap(__value_, __rhs.__value_); + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __swap(__optional_storage_base& __rhs) noexcept { + std::swap(__value_, __rhs.__value_); } }; From 5cf4603330dfbb5a45e79ce06a4c9fb76618e05a Mon Sep 17 00:00:00 2001 From: William Tran-Viet Date: Fri, 7 Nov 2025 21:24:33 -0500 Subject: [PATCH 29/39] Replace all usages of optional::value_type with _Tp, remove reference on value_type --- libcxx/include/optional | 105 ++++++++---------- .../optional/optional.object/types.pass.cpp | 9 +- 2 files changed, 56 insertions(+), 58 deletions(-) diff --git a/libcxx/include/optional b/libcxx/include/optional index 6953c858d3160..95be47e2487e8 100644 --- a/libcxx/include/optional +++ b/libcxx/include/optional @@ -687,24 +687,23 @@ class _LIBCPP_DECLSPEC_EMPTY_BASES optional using __base _LIBCPP_NODEBUG = __optional_move_assign_base<_Tp>; public: - using value_type = _Tp; + using value_type = __libcpp_remove_reference_t<_Tp>; using __trivially_relocatable _LIBCPP_NODEBUG = conditional_t<__libcpp_is_trivially_relocatable<_Tp>::value, optional, void>; using __replaceable _LIBCPP_NODEBUG = conditional_t<__is_replaceable_v<_Tp>, optional, void>; private: - static_assert(!is_same_v<__remove_cvref_t, in_place_t>, + static_assert(!is_same_v<__remove_cvref_t<_Tp>, in_place_t>, "instantiation of optional with in_place_t is ill-formed"); - static_assert(!is_same_v<__remove_cvref_t, nullopt_t>, - "instantiation of optional with nullopt_t is ill-formed"); + static_assert(!is_same_v<__remove_cvref_t<_Tp>, nullopt_t>, "instantiation of optional with nullopt_t is ill-formed"); # if _LIBCPP_STD_VER >= 26 static_assert(!is_rvalue_reference_v<_Tp>, "instantiation of optional with an rvalue reference type is ill-formed"); # else static_assert(!is_reference_v<_Tp>, "instantiation of optional with a reference type is ill-formed"); # endif - static_assert(is_destructible_v, "instantiation of optional with a non-destructible type is ill-formed"); - static_assert(!is_array_v, "instantiation of optional with an array type is ill-formed"); + static_assert(is_destructible_v<_Tp>, "instantiation of optional with a non-destructible type is ill-formed"); + static_assert(!is_array_v<_Tp>, "instantiation of optional with an array type is ill-formed"); # if _LIBCPP_STD_VER >= 26 template @@ -785,18 +784,15 @@ public: template , is_constructible>::value, int> = 0> + enable_if_t<_And<_IsSame<_InPlaceT, in_place_t>, is_constructible<_Tp, _Args...>>::value, int> = 0> _LIBCPP_HIDE_FROM_ABI constexpr explicit optional(_InPlaceT, _Args&&... __args) : __base(in_place, std::forward<_Args>(__args)...) {} - template &, _Args...>, int> = 0> + template &, _Args...>, int> = 0> _LIBCPP_HIDE_FROM_ABI constexpr explicit optional(in_place_t, initializer_list<_Up> __il, _Args&&... __args) : __base(in_place, __il, std::forward<_Args>(__args)...) {} - template ::template __enable_implicit<_Up>(), int> = 0> + template ::template __enable_implicit<_Up>(), int> = 0> _LIBCPP_HIDE_FROM_ABI constexpr optional(_Up&& __v) : __base(in_place, std::forward<_Up>(__v)) {} template , @@ -825,14 +821,11 @@ public: // deleted optional constructors # if _LIBCPP_STD_VER >= 26 - template &, _Args...>, int> = 0> + template &, _Args...>, int> = 0> requires __libcpp_opt_ref_ctor_deleted<_Up> _LIBCPP_HIDE_FROM_ABI constexpr explicit optional(in_place_t, initializer_list<_Up> __il, _Args&&... __args) = delete; - template ::template __enable_implicit<_Up>(), int> = 0> + template ::template __enable_implicit<_Up>(), int> = 0> requires __libcpp_opt_ref_ctor_deleted<_Up> _LIBCPP_HIDE_FROM_ABI constexpr optional(_Up&& __v) = delete; @@ -876,11 +869,11 @@ public: _LIBCPP_HIDE_FROM_ABI constexpr optional& operator=(optional&&) = default; // LWG2756 - template , + template , enable_if_t<_And<_IsNotSame<__remove_cvref_t<_Up>, optional>, - _Or<_IsNotSame<__remove_cvref_t<_Up>, value_type>, _Not>>, - is_constructible, - is_assignable>::value, + _Or<_IsNotSame<__remove_cvref_t<_Up>, _Tp>, _Not>>, + is_constructible<_Tp, _Up>, + is_assignable<_Tp&, _Up>>::value, int> = 0> _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 optional& operator=(_Up&& __v) { if (this->has_value()) @@ -904,7 +897,7 @@ public: return *this; } - template , int> = 0> + template , int> = 0> _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Tp& emplace(_Args&&... __args) { reset(); this->__construct(std::forward<_Args>(__args)...); @@ -913,7 +906,7 @@ public: template &, _Args...> + enable_if_t&, _Args...> # if _LIBCPP_STD_VER >= 26 && !reference_constructs_from_temporary_v<_Tp&, _Up> # endif @@ -926,7 +919,7 @@ public: } _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void - swap(optional& __opt) noexcept(is_nothrow_move_constructible_v && is_nothrow_swappable_v) { + swap(optional& __opt) noexcept(is_nothrow_move_constructible_v<_Tp> && is_nothrow_swappable_v<_Tp>) { if (this->has_value() == __opt.has_value()) { if (this->has_value()) this->__swap(__opt); @@ -941,32 +934,32 @@ public: } } - _LIBCPP_HIDE_FROM_ABI constexpr add_pointer_t operator->() const noexcept { + _LIBCPP_HIDE_FROM_ABI constexpr add_pointer_t<_Tp const> operator->() const noexcept { _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator-> called on a disengaged value"); return std::addressof(this->__get()); } - _LIBCPP_HIDE_FROM_ABI constexpr add_pointer_t operator->() noexcept { + _LIBCPP_HIDE_FROM_ABI constexpr add_pointer_t<_Tp> operator->() noexcept { _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator-> called on a disengaged value"); return std::addressof(this->__get()); } - _LIBCPP_HIDE_FROM_ABI constexpr const value_type& operator*() const& noexcept { + _LIBCPP_HIDE_FROM_ABI constexpr const _Tp& operator*() const& noexcept { _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator* called on a disengaged value"); return this->__get(); } - _LIBCPP_HIDE_FROM_ABI constexpr value_type& operator*() & noexcept { + _LIBCPP_HIDE_FROM_ABI constexpr _Tp& operator*() & noexcept { _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator* called on a disengaged value"); return this->__get(); } - _LIBCPP_HIDE_FROM_ABI constexpr value_type&& operator*() && noexcept { + _LIBCPP_HIDE_FROM_ABI constexpr _Tp&& operator*() && noexcept { _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator* called on a disengaged value"); return std::move(this->__get()); } - _LIBCPP_HIDE_FROM_ABI constexpr const value_type&& operator*() const&& noexcept { + _LIBCPP_HIDE_FROM_ABI constexpr const _Tp&& operator*() const&& noexcept { _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator* called on a disengaged value"); return std::move(this->__get()); } @@ -976,25 +969,25 @@ public: using __base::__get; using __base::has_value; - _LIBCPP_HIDE_FROM_ABI constexpr value_type const& value() const& { + _LIBCPP_HIDE_FROM_ABI constexpr _Tp const& value() const& { if (!this->has_value()) std::__throw_bad_optional_access(); return this->__get(); } - _LIBCPP_HIDE_FROM_ABI constexpr value_type& value() & { + _LIBCPP_HIDE_FROM_ABI constexpr _Tp& value() & { if (!this->has_value()) std::__throw_bad_optional_access(); return this->__get(); } - _LIBCPP_HIDE_FROM_ABI constexpr value_type&& value() && { + _LIBCPP_HIDE_FROM_ABI constexpr _Tp&& value() && { if (!this->has_value()) std::__throw_bad_optional_access(); return std::move(this->__get()); } - _LIBCPP_HIDE_FROM_ABI constexpr value_type const&& value() const&& { + _LIBCPP_HIDE_FROM_ABI constexpr _Tp const&& value() const&& { if (!this->has_value()) std::__throw_bad_optional_access(); return std::move(this->__get()); @@ -1005,37 +998,37 @@ public: requires(!(is_lvalue_reference_v<_Tp> && is_function_v<__libcpp_remove_reference_t<_Tp>>) && !(is_lvalue_reference_v<_Tp> && is_array_v<__libcpp_remove_reference_t<_Tp>>)) # endif - _LIBCPP_HIDE_FROM_ABI constexpr value_type value_or(_Up&& __v) const& { - static_assert(is_copy_constructible_v, "optional::value_or: T must be copy constructible"); - static_assert(is_convertible_v<_Up, value_type>, "optional::value_or: U must be convertible to T"); - return this->has_value() ? this->__get() : static_cast(std::forward<_Up>(__v)); + _LIBCPP_HIDE_FROM_ABI constexpr _Tp value_or(_Up&& __v) const& { + static_assert(is_copy_constructible_v<_Tp>, "optional::value_or: T must be copy constructible"); + static_assert(is_convertible_v<_Up, _Tp>, "optional::value_or: U must be convertible to T"); + return this->has_value() ? this->__get() : static_cast<_Tp>(std::forward<_Up>(__v)); } template > # if _LIBCPP_STD_VER >= 26 requires(!is_lvalue_reference_v<_Tp>) # endif - _LIBCPP_HIDE_FROM_ABI constexpr value_type value_or(_Up&& __v) && { - static_assert(is_move_constructible_v, "optional::value_or: T must be move constructible"); - static_assert(is_convertible_v<_Up, value_type>, "optional::value_or: U must be convertible to T"); - return this->has_value() ? std::move(this->__get()) : static_cast(std::forward<_Up>(__v)); + _LIBCPP_HIDE_FROM_ABI constexpr _Tp value_or(_Up&& __v) && { + static_assert(is_move_constructible_v<_Tp>, "optional::value_or: T must be move constructible"); + static_assert(is_convertible_v<_Up, _Tp>, "optional::value_or: U must be convertible to T"); + return this->has_value() ? std::move(this->__get()) : static_cast<_Tp>(std::forward<_Up>(__v)); } # if _LIBCPP_STD_VER >= 26 template > requires(is_lvalue_reference_v<_Tp> && !(is_function_v<__libcpp_remove_reference_t<_Tp>> || is_array_v<__libcpp_remove_reference_t<_Tp>>)) - _LIBCPP_HIDE_FROM_ABI constexpr value_type value_or(_Up&& __v) && { - static_assert(is_move_constructible_v, "optional::value_or: T must be move constructible"); - static_assert(is_convertible_v<_Up, value_type>, "optional::value_or: U must be convertible to T"); - return this->has_value() ? this->__get() : static_cast(std::forward<_Up>(__v)); + _LIBCPP_HIDE_FROM_ABI constexpr _Tp value_or(_Up&& __v) && { + static_assert(is_move_constructible_v<_Tp>, "optional::value_or: T must be move constructible"); + static_assert(is_convertible_v<_Up, _Tp>, "optional::value_or: U must be convertible to T"); + return this->has_value() ? this->__get() : static_cast<_Tp>(std::forward<_Up>(__v)); } # endif # if _LIBCPP_STD_VER >= 23 template _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) & { - using _Up = invoke_result_t<_Func, value_type&>; + using _Up = invoke_result_t<_Func, _Tp&>; static_assert(__is_std_optional>::value, "Result of f(value()) must be a specialization of std::optional"); if (*this) @@ -1045,7 +1038,7 @@ public: template _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) const& { - using _Up = invoke_result_t<_Func, const value_type&>; + using _Up = invoke_result_t<_Func, const _Tp&>; static_assert(__is_std_optional>::value, "Result of f(value()) must be a specialization of std::optional"); if (*this) @@ -1055,7 +1048,7 @@ public: template _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) && { - using _Up = invoke_result_t<_Func, value_type&&>; + using _Up = invoke_result_t<_Func, _Tp&&>; static_assert(__is_std_optional>::value, "Result of f(std::move(value())) must be a specialization of std::optional"); if (*this) @@ -1065,7 +1058,7 @@ public: template _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) const&& { - using _Up = invoke_result_t<_Func, const value_type&&>; + using _Up = invoke_result_t<_Func, const _Tp&&>; static_assert(__is_std_optional>::value, "Result of f(std::move(value())) must be a specialization of std::optional"); if (*this) @@ -1075,7 +1068,7 @@ public: template _LIBCPP_HIDE_FROM_ABI constexpr auto transform(_Func&& __f) & { - using _Up = remove_cv_t>; + using _Up = remove_cv_t>; static_assert(!is_array_v<_Up>, "Result of f(value()) should not be an Array"); static_assert(!is_same_v<_Up, in_place_t>, "Result of f(value()) should not be std::in_place_t"); static_assert(!is_same_v<_Up, nullopt_t>, "Result of f(value()) should not be std::nullopt_t"); @@ -1087,7 +1080,7 @@ public: template _LIBCPP_HIDE_FROM_ABI constexpr auto transform(_Func&& __f) const& { - using _Up = remove_cv_t>; + using _Up = remove_cv_t>; static_assert(!is_array_v<_Up>, "Result of f(value()) should not be an Array"); static_assert(!is_same_v<_Up, in_place_t>, "Result of f(value()) should not be std::in_place_t"); static_assert(!is_same_v<_Up, nullopt_t>, "Result of f(value()) should not be std::nullopt_t"); @@ -1099,7 +1092,7 @@ public: template _LIBCPP_HIDE_FROM_ABI constexpr auto transform(_Func&& __f) && { - using _Up = remove_cv_t>; + using _Up = remove_cv_t>; static_assert(!is_array_v<_Up>, "Result of f(std::move(value())) should not be an Array"); static_assert(!is_same_v<_Up, in_place_t>, "Result of f(std::move(value())) should not be std::in_place_t"); static_assert(!is_same_v<_Up, nullopt_t>, "Result of f(std::move(value())) should not be std::nullopt_t"); @@ -1111,7 +1104,7 @@ public: template _LIBCPP_HIDE_FROM_ABI constexpr auto transform(_Func&& __f) const&& { - using _Up = remove_cvref_t>; + using _Up = remove_cvref_t>; static_assert(!is_array_v<_Up>, "Result of f(std::move(value())) should not be an Array"); static_assert(!is_same_v<_Up, in_place_t>, "Result of f(std::move(value())) should not be std::in_place_t"); static_assert(!is_same_v<_Up, nullopt_t>, "Result of f(std::move(value())) should not be std::nullopt_t"); @@ -1123,7 +1116,7 @@ public: template _LIBCPP_HIDE_FROM_ABI constexpr optional or_else(_Func&& __f) const& - requires is_copy_constructible_v + requires is_copy_constructible_v<_Tp> { static_assert(is_same_v>, optional>, "Result of f() should be the same type as this optional"); @@ -1134,7 +1127,7 @@ public: template _LIBCPP_HIDE_FROM_ABI constexpr optional or_else(_Func&& __f) && - requires is_move_constructible_v + requires is_move_constructible_v<_Tp> { static_assert(is_same_v>, optional>, "Result of f() should be the same type as this optional"); diff --git a/libcxx/test/std/utilities/optional/optional.object/types.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/types.pass.cpp index d097559877267..ecbc6b4548ee6 100644 --- a/libcxx/test/std/utilities/optional/optional.object/types.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/types.pass.cpp @@ -36,6 +36,11 @@ int main(int, char**) test, const int>(); test, double>(); test, const double>(); - - return 0; +#if TEST_STD_VER >= 26 + test, int>(); + test, const int>(); + test, double>(); + test, const double>(); +#endif + return 0; } From 3c2473d15682ea094b7257e3838483370b5075fd Mon Sep 17 00:00:00 2001 From: William Tran-Viet Date: Fri, 7 Nov 2025 21:24:51 -0500 Subject: [PATCH 30/39] Clean up deleted constructors signatures --- libcxx/include/optional | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/libcxx/include/optional b/libcxx/include/optional index 95be47e2487e8..b79660de5167e 100644 --- a/libcxx/include/optional +++ b/libcxx/include/optional @@ -823,32 +823,32 @@ public: # if _LIBCPP_STD_VER >= 26 template &, _Args...>, int> = 0> requires __libcpp_opt_ref_ctor_deleted<_Up> - _LIBCPP_HIDE_FROM_ABI constexpr explicit optional(in_place_t, initializer_list<_Up> __il, _Args&&... __args) = delete; + explicit optional(in_place_t, initializer_list<_Up>, _Args&&...) = delete; template ::template __enable_implicit<_Up>(), int> = 0> requires __libcpp_opt_ref_ctor_deleted<_Up> - _LIBCPP_HIDE_FROM_ABI constexpr optional(_Up&& __v) = delete; + optional(_Up&&) = delete; template , enable_if_t<_CheckOptionalArgsCtor<_Up>::template __enable_explicit<_Up>(), int> = 0> requires __libcpp_opt_ref_ctor_deleted<_Up> - _LIBCPP_HIDE_FROM_ABI constexpr explicit optional(_Up&& __v) = delete; + explicit optional(_Up&&) = delete; template ::template __enable_implicit<_Up>(), int> = 0> requires __libcpp_opt_ref_ctor_deleted<_Up> - _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 optional(const optional<_Up>& __v) = delete; + optional(const optional<_Up>&) = delete; template ::template __enable_explicit<_Up>(), int> = 0> requires __libcpp_opt_ref_ctor_deleted<_Up> - _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit optional(const optional<_Up>& __v) = delete; + explicit optional(const optional<_Up>&) = delete; template ::template __enable_implicit<_Up>(), int> = 0> requires __libcpp_opt_ref_ctor_deleted<_Up> - _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 optional(optional<_Up>&& __v) = delete; + optional(optional<_Up>&&) = delete; template ::template __enable_explicit<_Up>(), int> = 0> requires __libcpp_opt_ref_ctor_deleted<_Up> - _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit optional(optional<_Up>&& __v) = delete; + explicit optional(optional<_Up>&&) = delete; # endif # if _LIBCPP_STD_VER >= 23 From 4f58bf290df6e35d6a96a0b7987c3c2c5e1a7001 Mon Sep 17 00:00:00 2001 From: William Tran-Viet Date: Sat, 8 Nov 2025 14:14:13 -0500 Subject: [PATCH 31/39] Drive-by: Add a ranges::range> test for iterators --- .../optional/optional.iterator/iterator.pass.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/libcxx/test/std/utilities/optional/optional.iterator/iterator.pass.cpp b/libcxx/test/std/utilities/optional/optional.iterator/iterator.pass.cpp index 34d0d6b135564..671fac35e732a 100644 --- a/libcxx/test/std/utilities/optional/optional.iterator/iterator.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.iterator/iterator.pass.cpp @@ -14,16 +14,23 @@ // template class optional::const_iterator; #include -#include #include #include #include #include +template +constexpr bool test_range_concept() { + return std::ranges::range>; +} + template __val> constexpr bool test() { std::remove_reference_t v{__val}; std::optional opt{v}; + { + assert(test_range_concept()); + } { // Dereferencing an iterator of an engaged optional will return the same value that the optional holds. auto it = opt.begin(); @@ -94,6 +101,10 @@ constexpr bool tests() { assert((test())); assert((test())); + assert(!test_range_concept()); + assert(!test_range_concept()); + assert(!test_range_concept()); + return true; } From ba998d73dbe9a2d107becaf5a0689d9a4d30ccd7 Mon Sep 17 00:00:00 2001 From: William Tran-Viet Date: Sat, 8 Nov 2025 14:15:25 -0500 Subject: [PATCH 32/39] Add enable_borrowed_range> and corresponding test --- libcxx/include/optional | 5 ++ .../optional.iterator/borrowed_range.pass.cpp | 74 +++++++++++++++++++ 2 files changed, 79 insertions(+) create mode 100644 libcxx/test/std/utilities/optional/optional.iterator/borrowed_range.pass.cpp diff --git a/libcxx/include/optional b/libcxx/include/optional index b79660de5167e..e439e4050daf8 100644 --- a/libcxx/include/optional +++ b/libcxx/include/optional @@ -210,6 +210,7 @@ namespace std { # include <__iterator/wrap_iter.h> # include <__memory/addressof.h> # include <__memory/construct_at.h> +# include <__ranges/enable_borrowed_range.h> # include <__ranges/enable_view.h> # include <__tuple/sfinae_helpers.h> # include <__type_traits/add_pointer.h> @@ -594,6 +595,10 @@ constexpr bool ranges::enable_view> = true; template constexpr range_format format_kind> = range_format::disabled; + +template +constexpr bool ranges::enable_borrowed_range> = true; + # endif # if _LIBCPP_STD_VER >= 20 diff --git a/libcxx/test/std/utilities/optional/optional.iterator/borrowed_range.pass.cpp b/libcxx/test/std/utilities/optional/optional.iterator/borrowed_range.pass.cpp new file mode 100644 index 0000000000000..8ed719a534166 --- /dev/null +++ b/libcxx/test/std/utilities/optional/optional.iterator/borrowed_range.pass.cpp @@ -0,0 +1,74 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++26 + +// + +// template class optional::iterator; +// template class optional::const_iterator; +// template +// constexpr bool ranges::enable_borrowed_range> = true; + +#include +#include +#include + +template +constexpr bool enable_borrowed_range() { + { + assert(std::ranges::enable_borrowed_range>); + } + return true; +} + +template +constexpr bool borrowed_range() { + if (std::ranges::range>) { + assert(std::ranges::borrowed_range>); + } else { + assert(!std::ranges::borrowed_range>); + return false; + } + + return true; +} + +constexpr bool test_enable_borrowed_range() { + assert(enable_borrowed_range()); + assert(enable_borrowed_range()); + assert(enable_borrowed_range()); + assert(enable_borrowed_range()); + assert(enable_borrowed_range()); + + return true; +} + +constexpr bool test_borrowed_range() { + assert(borrowed_range()); + assert(borrowed_range()); + assert(!borrowed_range()); + assert(!borrowed_range()); + assert(!borrowed_range()); + + return true; +} + +int main(int, char**) { + { + static_assert(test_enable_borrowed_range()); + assert(test_enable_borrowed_range()); + } + + { + static_assert(test_borrowed_range()); + assert(test_enable_borrowed_range()); + } + + return 0; +} \ No newline at end of file From 44d8d05fd5899d33f0b7da9a25bd01cb52e27f50 Mon Sep 17 00:00:00 2001 From: William Tran-Viet Date: Sat, 8 Nov 2025 14:21:01 -0500 Subject: [PATCH 33/39] Add to module inc --- libcxx/modules/std/optional.inc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libcxx/modules/std/optional.inc b/libcxx/modules/std/optional.inc index 9ee51117277ce..88de0bb4db12b 100644 --- a/libcxx/modules/std/optional.inc +++ b/libcxx/modules/std/optional.inc @@ -13,8 +13,9 @@ export namespace std { #if _LIBCPP_STD_VER >= 26 // [optional.iterators], iterator support namespace ranges { + using std::ranges::enable_borrowed_range; using std::ranges::enable_view; - } + } // namespace ranges #endif // [optional.nullopt], no-value state indicator using std::nullopt; From 1cd8ebe3d43d6cdefab472a27d3811e9bb87e298 Mon Sep 17 00:00:00 2001 From: William Tran-Viet Date: Sun, 9 Nov 2025 12:44:29 -0500 Subject: [PATCH 34/39] Make the borrowed_range test compile only --- .../borrowed_range.compile.pass.cpp | 53 +++++++++++++ .../optional.iterator/borrowed_range.pass.cpp | 74 ------------------- 2 files changed, 53 insertions(+), 74 deletions(-) create mode 100644 libcxx/test/std/utilities/optional/optional.iterator/borrowed_range.compile.pass.cpp delete mode 100644 libcxx/test/std/utilities/optional/optional.iterator/borrowed_range.pass.cpp diff --git a/libcxx/test/std/utilities/optional/optional.iterator/borrowed_range.compile.pass.cpp b/libcxx/test/std/utilities/optional/optional.iterator/borrowed_range.compile.pass.cpp new file mode 100644 index 0000000000000..98e470caf6fb2 --- /dev/null +++ b/libcxx/test/std/utilities/optional/optional.iterator/borrowed_range.compile.pass.cpp @@ -0,0 +1,53 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++26 + +// + +// template class optional::iterator; +// template class optional::const_iterator; +// template +// constexpr bool ranges::enable_borrowed_range> = true; + +#include +#include +#include + +template +constexpr void enable_borrowed_range() { + static_assert(std::ranges::enable_borrowed_range>); +} + +template +constexpr void borrowed_range() { + static_assert(std::ranges::range> == std::ranges::borrowed_range>); +} + +constexpr void test_enable_borrowed_range() { + enable_borrowed_range(); + enable_borrowed_range(); + enable_borrowed_range(); + enable_borrowed_range(); + enable_borrowed_range(); +} + +constexpr void test_borrowed_range() { + borrowed_range(); + borrowed_range(); + borrowed_range(); + borrowed_range(); + borrowed_range(); +} + +int main(int, char**) { + test_enable_borrowed_range(); + test_borrowed_range(); + + return 0; +} diff --git a/libcxx/test/std/utilities/optional/optional.iterator/borrowed_range.pass.cpp b/libcxx/test/std/utilities/optional/optional.iterator/borrowed_range.pass.cpp deleted file mode 100644 index 8ed719a534166..0000000000000 --- a/libcxx/test/std/utilities/optional/optional.iterator/borrowed_range.pass.cpp +++ /dev/null @@ -1,74 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -// REQUIRES: std-at-least-c++26 - -// - -// template class optional::iterator; -// template class optional::const_iterator; -// template -// constexpr bool ranges::enable_borrowed_range> = true; - -#include -#include -#include - -template -constexpr bool enable_borrowed_range() { - { - assert(std::ranges::enable_borrowed_range>); - } - return true; -} - -template -constexpr bool borrowed_range() { - if (std::ranges::range>) { - assert(std::ranges::borrowed_range>); - } else { - assert(!std::ranges::borrowed_range>); - return false; - } - - return true; -} - -constexpr bool test_enable_borrowed_range() { - assert(enable_borrowed_range()); - assert(enable_borrowed_range()); - assert(enable_borrowed_range()); - assert(enable_borrowed_range()); - assert(enable_borrowed_range()); - - return true; -} - -constexpr bool test_borrowed_range() { - assert(borrowed_range()); - assert(borrowed_range()); - assert(!borrowed_range()); - assert(!borrowed_range()); - assert(!borrowed_range()); - - return true; -} - -int main(int, char**) { - { - static_assert(test_enable_borrowed_range()); - assert(test_enable_borrowed_range()); - } - - { - static_assert(test_borrowed_range()); - assert(test_enable_borrowed_range()); - } - - return 0; -} \ No newline at end of file From d32557c8f73d7eb46b89f58cc42b628921e32a54 Mon Sep 17 00:00:00 2001 From: William Tran-Viet Date: Sun, 9 Nov 2025 23:42:32 -0500 Subject: [PATCH 35/39] Remove std:: prefixes in iterator struct --- libcxx/include/optional | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/libcxx/include/optional b/libcxx/include/optional index e439e4050daf8..2a242b90fc0db 100644 --- a/libcxx/include/optional +++ b/libcxx/include/optional @@ -622,8 +622,8 @@ struct __optional_iterator< enable_if_t && is_function_v<__libcpp_remove_reference_t<_Tp>>) && !(is_lvalue_reference_v<_Tp> && is_array_v<__libcpp_remove_reference_t<_Tp>>)> > { private: - using __pointer _LIBCPP_NODEBUG = std::add_pointer_t>; - using __const_pointer _LIBCPP_NODEBUG = std::add_pointer_t>; + using __pointer _LIBCPP_NODEBUG = add_pointer_t>; + using __const_pointer _LIBCPP_NODEBUG = add_pointer_t>; public: # if _LIBCPP_STD_VER >= 26 @@ -640,16 +640,16 @@ public: auto& __derived_self = static_cast&>(*this); auto __ptr = [&__derived_self]() { if constexpr (is_lvalue_reference_v<_Tp>) { - return __derived_self.has_value() ? std::addressof(__derived_self.__get()) : nullptr; + return __derived_self.has_value() ? addressof(__derived_self.__get()) : nullptr; } - return std::addressof(__derived_self.__get()); + return addressof(__derived_self.__get()); }(); # ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL - return std::__make_bounded_iter( - std::__wrap_iter<__pointer>(__ptr), - std::__wrap_iter<__pointer>(__ptr), - std::__wrap_iter<__pointer>(__ptr) + (__derived_self.has_value() ? 1 : 0)); + return __make_bounded_iter( + __wrap_iter<__pointer>(__ptr), + __wrap_iter<__pointer>(__ptr), + __wrap_iter<__pointer>(__ptr) + (__derived_self.has_value() ? 1 : 0)); # else return iterator(__ptr); # endif @@ -659,16 +659,16 @@ public: auto& __derived_self = static_cast&>(*this); auto* __ptr = [&__derived_self]() { if constexpr (is_lvalue_reference_v<_Tp>) { - return __derived_self.has_value() ? std::addressof(__derived_self.__get()) : nullptr; + return __derived_self.has_value() ? addressof(__derived_self.__get()) : nullptr; } - return std::addressof(__derived_self.__get()); + return addressof(__derived_self.__get()); }(); # ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL - return std::__make_bounded_iter( - std::__wrap_iter<__const_pointer>(__ptr), - std::__wrap_iter<__const_pointer>(__ptr), - std::__wrap_iter<__const_pointer>(__ptr) + (__derived_self.has_value() ? 1 : 0)); + return __make_bounded_iter( + __wrap_iter<__const_pointer>(__ptr), + __wrap_iter<__const_pointer>(__ptr), + __wrap_iter<__const_pointer>(__ptr) + (__derived_self.has_value() ? 1 : 0)); # else return const_iterator(__ptr); # endif From 2d327fd5466c7a8c1fdf70b7608ce50e406cbb28 Mon Sep 17 00:00:00 2001 From: William Tran-Viet Date: Mon, 10 Nov 2025 00:01:54 -0500 Subject: [PATCH 36/39] Simplify borrowed_range test --- .../borrowed_range.compile.pass.cpp | 23 ++----------------- 1 file changed, 2 insertions(+), 21 deletions(-) diff --git a/libcxx/test/std/utilities/optional/optional.iterator/borrowed_range.compile.pass.cpp b/libcxx/test/std/utilities/optional/optional.iterator/borrowed_range.compile.pass.cpp index 98e470caf6fb2..9d62ab9dd5b44 100644 --- a/libcxx/test/std/utilities/optional/optional.iterator/borrowed_range.compile.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.iterator/borrowed_range.compile.pass.cpp @@ -19,35 +19,16 @@ #include #include -template -constexpr void enable_borrowed_range() { - static_assert(std::ranges::enable_borrowed_range>); -} - template constexpr void borrowed_range() { + static_assert(std::ranges::enable_borrowed_range>); static_assert(std::ranges::range> == std::ranges::borrowed_range>); } -constexpr void test_enable_borrowed_range() { - enable_borrowed_range(); - enable_borrowed_range(); - enable_borrowed_range(); - enable_borrowed_range(); - enable_borrowed_range(); -} - -constexpr void test_borrowed_range() { +void test_borrowed_range() { borrowed_range(); borrowed_range(); borrowed_range(); borrowed_range(); borrowed_range(); } - -int main(int, char**) { - test_enable_borrowed_range(); - test_borrowed_range(); - - return 0; -} From 880558a9cce5573aa7d31bda4f67f7b2198b1845 Mon Sep 17 00:00:00 2001 From: William Tran-Viet Date: Mon, 10 Nov 2025 00:02:17 -0500 Subject: [PATCH 37/39] ditto --- .../optional/optional.iterator/borrowed_range.compile.pass.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libcxx/test/std/utilities/optional/optional.iterator/borrowed_range.compile.pass.cpp b/libcxx/test/std/utilities/optional/optional.iterator/borrowed_range.compile.pass.cpp index 9d62ab9dd5b44..a79d1d51a5b11 100644 --- a/libcxx/test/std/utilities/optional/optional.iterator/borrowed_range.compile.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.iterator/borrowed_range.compile.pass.cpp @@ -20,7 +20,7 @@ #include template -constexpr void borrowed_range() { +void borrowed_range() { static_assert(std::ranges::enable_borrowed_range>); static_assert(std::ranges::range> == std::ranges::borrowed_range>); } From 995bb756812eb9c1af423e42ff1f6c35ac6fad3b Mon Sep 17 00:00:00 2001 From: William Tran-Viet Date: Mon, 10 Nov 2025 00:26:00 -0500 Subject: [PATCH 38/39] ADL lookup --- libcxx/include/optional | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libcxx/include/optional b/libcxx/include/optional index 2a242b90fc0db..9e5cb6dda5173 100644 --- a/libcxx/include/optional +++ b/libcxx/include/optional @@ -640,9 +640,9 @@ public: auto& __derived_self = static_cast&>(*this); auto __ptr = [&__derived_self]() { if constexpr (is_lvalue_reference_v<_Tp>) { - return __derived_self.has_value() ? addressof(__derived_self.__get()) : nullptr; + return __derived_self.has_value() ? std::addressof(__derived_self.__get()) : nullptr; } - return addressof(__derived_self.__get()); + return std::addressof(__derived_self.__get()); }(); # ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL @@ -659,9 +659,9 @@ public: auto& __derived_self = static_cast&>(*this); auto* __ptr = [&__derived_self]() { if constexpr (is_lvalue_reference_v<_Tp>) { - return __derived_self.has_value() ? addressof(__derived_self.__get()) : nullptr; + return __derived_self.has_value() ? std::addressof(__derived_self.__get()) : nullptr; } - return addressof(__derived_self.__get()); + return std::addressof(__derived_self.__get()); }(); # ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL From cddffc5eb86f93779d2d193e2f3c57f732c519b9 Mon Sep 17 00:00:00 2001 From: William Tran-Viet Date: Mon, 10 Nov 2025 09:23:00 -0500 Subject: [PATCH 39/39] ADL lookup --- libcxx/include/optional | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libcxx/include/optional b/libcxx/include/optional index 9e5cb6dda5173..9507819e07612 100644 --- a/libcxx/include/optional +++ b/libcxx/include/optional @@ -646,7 +646,7 @@ public: }(); # ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL - return __make_bounded_iter( + return std::__make_bounded_iter( __wrap_iter<__pointer>(__ptr), __wrap_iter<__pointer>(__ptr), __wrap_iter<__pointer>(__ptr) + (__derived_self.has_value() ? 1 : 0)); @@ -665,7 +665,7 @@ public: }(); # ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL - return __make_bounded_iter( + return std::__make_bounded_iter( __wrap_iter<__const_pointer>(__ptr), __wrap_iter<__const_pointer>(__ptr), __wrap_iter<__const_pointer>(__ptr) + (__derived_self.has_value() ? 1 : 0));