[libcxx-commits] [libcxx] [libc++] Implement P2988R12: `std::optional<T&>` (PR #155202)
William Tran-Viet via libcxx-commits
libcxx-commits at lists.llvm.org
Wed Sep 24 20:53:01 PDT 2025
https://github.com/smallp-o-p updated https://github.com/llvm/llvm-project/pull/155202
>From 08a70ae1c0325071ac1cecf4dd57e6934758fe45 Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Fri, 22 Aug 2025 18:15:37 -0400
Subject: [PATCH 01/13] Update documentation
---
libcxx/docs/FeatureTestMacroTable.rst | 2 ++
libcxx/docs/ReleaseNotes/22.rst | 6 +++---
libcxx/docs/Status/Cxx2cPapers.csv | 2 +-
libcxx/utils/generate_feature_test_macro_components.py | 1 +
4 files changed, 7 insertions(+), 4 deletions(-)
diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 358889d8dbc37..c10fc4c365407 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -480,6 +480,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 509ead64ee525..5c3b575886a4f 100644
--- a/libcxx/docs/ReleaseNotes/22.rst
+++ b/libcxx/docs/ReleaseNotes/22.rst
@@ -38,9 +38,9 @@ What's New in Libc++ 22.0.0?
Implemented Papers
------------------
-- P2321R2: ``zip`` (`Github <https://llvm.org/PR105169>`__) (The paper is partially implemented. ``zip_transform_view``
- is implemented in this release)
-- P3168R2: Give ``std::optional`` Range Support (`Github <https://llvm.org/PR105430>`__)
+- P2321R2: ``zip`` (`Github <https://github.com/llvm/llvm-project/issues/105169>`__) (The paper is partially implemented. ``zip_transform_view`` is implemented in this release)
+- P2988R12: ``std::optional<T&>`` (`Github <https://github.com/llvm/llvm-project/issues/148131>`__)
+- P3168R2: Give ``std::optional`` Range Support (`Github <https://github.com/llvm/llvm-project/issues/105430>`__)
Improvements and New Features
-----------------------------
diff --git a/libcxx/docs/Status/Cxx2cPapers.csv b/libcxx/docs/Status/Cxx2cPapers.csv
index f873d16808afe..b143d4133fa5b 100644
--- a/libcxx/docs/Status/Cxx2cPapers.csv
+++ b/libcxx/docs/Status/Cxx2cPapers.csv
@@ -122,7 +122,7 @@
"`P3293R3 <https://wg21.link/P3293R3>`__","Splicing a base class subobject","2025-06 (Sofia)","","","`#148125 <https://github.com/llvm/llvm-project/issues/148125>`__",""
"`P3491R3 <https://wg21.link/P3491R3>`__","``define_static_{string,object,array}``","2025-06 (Sofia)","","","`#148126 <https://github.com/llvm/llvm-project/issues/148126>`__",""
"`P3096R12 <https://wg21.link/P3096R12>`__","Function Parameter Reflection in Reflection for C++26","2025-06 (Sofia)","","","`#148127 <https://github.com/llvm/llvm-project/issues/148127>`__",""
-"`P2988R12 <https://wg21.link/P2988R12>`__","``std::optional<T&>``","2025-06 (Sofia)","","","`#148131 <https://github.com/llvm/llvm-project/issues/148131>`__",""
+"`P2988R12 <https://wg21.link/P2988R12>`__","``std::optional<T&>``","2025-06 (Sofia)","|Complete|","22","`#148131 <https://github.com/llvm/llvm-project/issues/148131>`__",""
"`P3348R4 <https://wg21.link/P3348R4>`__","C++26 should refer to C23 not C17","2025-06 (Sofia)","","","`#148133 <https://github.com/llvm/llvm-project/issues/148133>`__",""
"`P3037R6 <https://wg21.link/P3037R6>`__","``constexpr`` ``std::shared_ptr`` and friends","2025-06 (Sofia)","","","`#148135 <https://github.com/llvm/llvm-project/issues/148135>`__",""
"`P3284R4 <https://wg21.link/P3284R4>`__","``write_env`` and ``unstoppable`` Sender Adaptors","2025-06 (Sofia)","","","`#148136 <https://github.com/llvm/llvm-project/issues/148136>`__",""
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index c1e579c775746..acee825e067c4 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -1005,6 +1005,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<T&>
},
"headers": ["optional"],
},
>From bb37110a81f73c7c34a15cb71855e26b94103bb6 Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Fri, 22 Aug 2025 19:46:38 -0400
Subject: [PATCH 02/13] 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 7610586ddecbb..8e5c7388812d3 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 <class _Tp, size_t _Size>
friend struct array;
- template <class _Tp>
- friend class optional;
+ template <class _Tp, class>
+ friend struct __optional_iterator;
};
template <class _Iter1>
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<T&> 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 <class _Tp>
struct __optional_storage_base<_Tp, true> {
using value_type = _Tp;
@@ -607,19 +604,19 @@ struct __is_std_optional : false_type {};
template <class _Tp>
struct __is_std_optional<optional<_Tp>> : true_type {};
-template <class _Tp>
-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 <class _Tp, class = void>
+struct __optional_iterator {};
- using __pointer _LIBCPP_NODEBUG = std::add_pointer_t<_Tp>;
- using __const_pointer _LIBCPP_NODEBUG = std::add_pointer_t<const _Tp>;
+template <class _Tp>
+struct __optional_iterator<
+ _Tp,
+ enable_if_t<!(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>>)> > {
+private:
+ using __pointer _LIBCPP_NODEBUG = std::add_pointer_t<remove_reference_t<_Tp>>;
+ using __const_pointer _LIBCPP_NODEBUG = std::add_pointer_t<const remove_reference_t<_Tp>>;
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<optional<_Tp>&>(*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<const optional<_Tp>&>(*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<optional<_Tp>&>(*this).has_value() ? 1 : 0);
+ }
+ _LIBCPP_HIDE_FROM_ABI constexpr const_iterator end() const noexcept {
+ return begin() + (static_cast<const optional<_Tp>&>(*this).has_value() ? 1 : 0);
+ }
# endif
+};
+
+template <class _Tp>
+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<value_type>, in_place_t>,
"instantiation of optional with in_place_t is ill-formed");
static_assert(!is_same_v<__remove_cvref_t<value_type>, nullopt_t>,
"instantiation of optional with nullopt_t is ill-formed");
- static_assert(!is_reference_v<value_type>, "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<value_type>, "instantiation of optional with a non-destructible type is ill-formed");
static_assert(!is_array_v<value_type>, "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<value_type const> 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 <class _Up = remove_cv_t<_Tp>>
+ template <class _Up = remove_cv_t<_Tp> = 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<value_type>, "optional<T>::value_or: T must be copy constructible");
static_assert(is_convertible_v<_Up, value_type>, "optional<T>::value_or: U must be convertible to T");
return this->has_value() ? this->__get() : static_cast<value_type>(std::forward<_Up>(__v));
}
- template <class _Up = remove_cv_t<_Tp>>
+ template <class _Up = remove_cv_t<_Tp> = 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<value_type>, "optional<T>::value_or: T must be move constructible");
static_assert(is_convertible_v<_Up, value_type>, "optional<T>::value_or: U must be convertible to T");
return this->has_value() ? std::move(this->__get()) : static_cast<value_type>(std::forward<_Up>(__v));
}
+# if _LIBCPP_STD_VER >= 26
+ template <class _Up = remove_cv_t<_Tp>>
+ 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<value_type>, "optional<T>::value_or: T must be move constructible");
+ static_assert(is_convertible_v<_Up, value_type>, "optional<T>::value_or: U must be convertible to T");
+ return this->has_value() ? this->__get() : static_cast<value_type>(std::forward<_Up>(__v));
+ }
+# endif
+
# if _LIBCPP_STD_VER >= 23
template <class _Func>
_LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) & {
>From 0828f8612c1579b679b6ef5d4b635b1e827381c8 Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Sat, 23 Aug 2025 00:51:45 -0400
Subject: [PATCH 03/13] 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<std::optional<int>>);
static_assert(has_iterator_aliases<std::optional<const int>>);
-
-// TODO: Uncomment these once P2988R12 is implemented, as they would be testing optional<T&>
-
-// static_assert(!has_iterator_aliases<std::optional<int (&)[]>>);
-// static_assert(!has_iterator_aliases<std::optional<void (&)(int, char)>>);
+static_assert(has_iterator_aliases<std::optional<int&>>);
+static_assert(has_iterator_aliases<std::optional<const int&>>);
+static_assert(!has_iterator_aliases<std::optional<int(&)[1]>>);
+static_assert(!has_iterator_aliases<std::optional<int(&)()>>);
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
+
+// <optional>
+
+// template <class U> T optional<T>::value_or(U&&);
+
+#include <optional>
+
+template <typename Opt, typename T>
+concept has_value_or = requires(Opt opt, T&& t) {
+ { opt.value_or(t) } -> std::same_as<T>;
+};
+
+static_assert(has_value_or<std::optional<int>, int>);
+static_assert(has_value_or<std::optional<int&>, int&>);
+static_assert(has_value_or<std::optional<const int&>, const int&>);
+static_assert(!has_value_or<std::optional<int(&)[1]>&&, int(&)[1]>);
+static_assert(!has_value_or<std::optional<int(&)()>&&, 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 <typename T>
constexpr bool test() {
- std::optional<T> opt{T{}};
+ std::remove_reference_t<T> t = std::remove_reference_t<T>{};
+ std::optional<T> opt{t};
{ // begin() is marked noexcept
static_assert(noexcept(opt.begin()));
@@ -53,6 +54,10 @@ constexpr bool tests() {
assert(test<char>());
assert(test<const int>());
assert(test<const char>());
+ assert(test<int&>());
+ assert(test<char&>());
+ assert(test<const int&>());
+ assert(test<const char&>());
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 <iterator>
#include <optional>
#include <ranges>
+#include <type_traits>
#include <utility>
template <typename T>
@@ -41,7 +42,8 @@ constexpr bool test() {
assert(it2 == std::as_const(disengaged).end());
}
- std::optional<T> engaged{T{}};
+ std::remove_reference_t<T> t = std::remove_reference_t<T>{};
+ std::optional<T> engaged{t};
{ // end() != begin() if the optional is engaged
auto it = engaged.end();
@@ -62,6 +64,10 @@ constexpr bool tests() {
assert(test<char>());
assert(test<const int>());
assert(test<const char>());
+ assert(test<int&>());
+ assert(test<char&>());
+ assert(test<const int&>());
+ assert(test<const char&>());
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 <type_traits>
#include <utility>
-template <typename T, T __val>
+template <typename T, std::remove_reference_t<T> __val>
constexpr bool test() {
- std::optional<T> opt{__val};
+ std::remove_reference_t<T> v{__val};
+ std::optional<T> 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<decltype(it2)>);
}
- { // const_iterator::value_type == std::remove_cv_t<T>, const_iterator::reference == const T&, iterator::value_type = std::remove_cv_t<T>, iterator::reference == T&
+ { // const_iterator::value_type == std::remove_cvref_t<T>, const_iterator::reference == const T&, iterator::value_type = std::remove_cvref_t<T>, iterator::reference == T&
+ // std::remove_cv_t is impossible for optional<T&>
auto it = opt.begin();
auto it2 = std::as_const(opt).begin();
- assert((std::is_same_v<typename decltype(it)::value_type, std::remove_cv_t<T>>));
- assert((std::is_same_v<typename decltype(it)::reference, T&>));
- assert((std::is_same_v<typename decltype(it2)::value_type, std::remove_cv_t<T>>));
- assert((std::is_same_v<typename decltype(it2)::reference, const T&>));
+ assert((std::is_same_v<typename decltype(it)::value_type, std::remove_cvref_t<T>>));
+ assert((std::is_same_v<typename decltype(it)::reference, std::remove_reference_t<T>&>));
+ assert((std::is_same_v<typename decltype(it2)::value_type, std::remove_cvref_t<T>>));
+ assert((std::is_same_v<typename decltype(it2)::reference, const std::remove_reference_t<T>&>));
}
{ // std::ranges::size for an engaged optional<T> == 1, disengaged optional<T> == 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<T> val{__val};
+ std::optional<T> 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<bool, true>()));
assert((test<const int, 2>()));
assert((test<const char, 'b'>()));
+ assert((test<int&, 1>()));
+ assert((test<char&, 'a'>()));
+ assert((test<bool&, true>()));
+ assert((test<const int&, 2>()));
+ assert((test<const char&, 'b'>()));
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<int&> 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<int>);
+ }
+
+ //With & qualifier on F's operator()
+ {
+ int j = 42;
+ std::optional<int&> 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<int>);
+ }
+ }
+
+ // Test const& overload
+ {
+ // Without & qualifier on F's operator()
+ {
+ int j = 42;
+ std::optional<const int&> 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<int>);
+ }
+
+ //With & qualifier on F's operator()
+ {
+ int j = 42;
+ const std::optional<int&> 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<int>);
+ }
+ }
+
+ // Test && overload
+ {
+
+ //With & qualifier on F's operator()
+ {
+ int j = 42;
+ std::optional<int&> 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<int>);
+ }
+ }
+
+ // Test const&& overload
+ {
+ //With & qualifier on F's operator()
+ {
+ int j = 42;
+ const std::optional<int&> 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<int>);
+ }
+ }
+ 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<MoveOnly>{};
});
}
+#if TEST_STD_VER >= 26
+ {
+ int i = 2;
+ std::optional<int&> opt;
+ assert(opt.or_else([&] { return std::optional<int&>{i}; }) == i);
+ int j = 3;
+ opt = j;
+ opt.or_else([] {
+ assert(false);
+ return std::optional<int&>{};
+ });
+ assert(opt == j);
+ }
+ {
+ int i = 2;
+ std::optional<int&> opt;
+ assert(std::move(opt).or_else([&] { return std::optional<int&>{i}; }) == i);
+ int j = 3;
+ opt = j;
+ std::move(opt).or_else([] {
+ assert(false);
+ return std::optional<int&>{};
+ });
+ 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 <cassert>
#include <optional>
#include <type_traits>
+#include <utility>
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<int&> opt1;
+ auto opt1r = opt1.transform([](int i) { return i + 2; });
+ assert(!opt1);
+ ASSERT_SAME_TYPE(decltype(opt1r), std::optional<int>);
+ }
+
+ {
+ int i = 42;
+ std::optional<int&> opt{i};
+ auto o2 = opt.transform([](int j) { return j + 2; });
+ assert(*o2 == 44);
+ ASSERT_SAME_TYPE(decltype(o2), std::optional<int>);
+ }
+ // Test & overload
+ {
+ // Without & qualifier on F's operator()
+ {
+ int i = 42;
+ std::optional<int&> opt{i};
+ auto o3 = opt.transform(LVal{});
+ assert(*o3 == 1);
+ ASSERT_SAME_TYPE(decltype(o3), std::optional<int>);
+ }
+
+ //With & qualifier on F's operator()
+ {
+ int i = 42;
+ std::optional<int&> opt{i};
+ RefQual l{};
+ auto o3 = opt.transform(l);
+ assert(*o3 == 1);
+ ASSERT_SAME_TYPE(decltype(o3), std::optional<int>);
+ }
+ }
+ // const& overload
+ {
+ // Without & qualifier on F's operator()
+ {
+ int i = 42;
+ std::optional<const int&> opt{i};
+ auto o3 = std::as_const(opt).transform(CLVal{});
+ assert(*o3 == 1);
+ ASSERT_SAME_TYPE(decltype(o3), std::optional<int>);
+ }
+
+ //With & qualifier on F's operator()
+ {
+ int i = 42;
+ const std::optional<int&> opt{i};
+ const CRefQual l{};
+ auto o3 = opt.transform(l);
+ assert(*o3 == 1);
+ ASSERT_SAME_TYPE(decltype(o3), std::optional<int>);
+ }
+ }
+
+ // Test && overload
+ {
+ // Without & qualifier on F's operator()
+ {
+ int i = 42;
+ std::optional<int> opt{i};
+ auto o3 = std::move(opt).transform(RVal{});
+ assert(*o3 == 1);
+ ASSERT_SAME_TYPE(decltype(o3), std::optional<int>);
+ }
+
+ //With & qualifier on F's operator()
+ {
+ int i = 42;
+ std::optional<int&> opt{i};
+ auto o3 = std::move(opt).transform(RVRefQual{});
+ assert(*o3 == 1);
+ ASSERT_SAME_TYPE(decltype(o3), std::optional<int>);
+ }
+ }
+
+ // const&& overload
+ {
+ //With & qualifier on F's operator()
+ {
+ int i = 42;
+ std::optional<int&> opt{i};
+ const RVCRefQual rvc{};
+ auto o3 = opt.transform(std::move(rvc));
+ assert(*o3 == 1);
+ ASSERT_SAME_TYPE(decltype(o3), std::optional<int>);
+ }
+ }
+ {
+ std::optional<int&> 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 <typename T, std::remove_reference_t<T> _Val>
+constexpr void test_with_ref() {
+ T t{_Val};
+ { // to empty
+ optional<T&> opt;
+ opt = t;
+ assert(static_cast<bool>(opt) == true);
+ assert(*opt == t);
+ }
+ { // to existing
+ optional<T&> opt{t};
+ opt = t;
+ assert(static_cast<bool>(opt) == true);
+ assert(*opt == t);
+ }
+ { // test default argument
+ optional<T&> opt;
+ opt = {t};
+ assert(static_cast<bool>(opt) == true);
+ assert(*opt == t);
+ }
+ { // test default argument
+ optional<T&> opt{t};
+ opt = {};
+ assert(static_cast<bool>(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<int, 3>();
+#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 <class T, std::remove_reference_t<T> _Val>
+constexpr bool test_ref() {
+ using Opt = std::optional<T&>;
+ T t{_Val};
+ {
+ Opt opt;
+ auto & v = opt.emplace(t);
+ static_assert(std::is_same_v<T&, decltype(v)>, "");
+ assert(static_cast<bool>(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<int, 1>());
+ static_assert(test_ref<double, 15.0>());
+ assert((test_ref<int, 1>()));
+ assert((test_ref<double, 15.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 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<int&&> opt2; // expected-error-re at optional:* {{static assertion failed{{.*}}instantiation of optional with an rvalue reference type is ill-formed}}
+#else
std::optional<char &> o1; // expected-error-re at optional:* {{static assertion failed{{.*}}instantiation of optional with a reference type is ill-formed}}
+#endif
std::optional<NonDestructible> o2; // expected-error-re at optional:* {{static assertion failed{{.*}}instantiation of optional with a non-destructible type is ill-formed}}
std::optional<char[20]> o3; // expected-error-re at 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
+
+// <optional>
+
+#include <cassert>
+#include <optional>
+#include <type_traits>
+#include <utility>
+
+template <typename RefType, std::remove_reference_t<RefType> _Val>
+constexpr bool test() {
+ std::remove_reference_t<RefType> item{_Val};
+ std::optional<RefType> 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<int&, 1>()));
+ static_assert((test<double&, 1.0>()));
+ assert((test<int&, 1>()));
+ assert((test<double&, 1.0>()));
+}
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<T>::value, "");
+ static_assert(std::is_trivially_destructible<optional<T>>::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<X&> opt(x);
+ X::dtor_called = false;
+ opt.reset();
+ assert(X::dtor_called == false);
+ assert(static_cast<bool>(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<X> opt(X{});
assert((*opt).test() == 4);
}
+#if TEST_STD_VER >= 26
+ {
+ X x{};
+ optional<X&> opt(x);
+ ASSERT_SAME_TYPE(decltype(*opt), X&);
+ ASSERT_NOEXCEPT(*opt);
+ }
+ {
+ X x{};
+ optional<X&> 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<X> opt(X{});
static_assert((*opt).test() == 3, "");
}
+#if TEST_STD_VER >= 26
+ {
+ X x{};
+ const optional<X&> opt{x};
+ ASSERT_SAME_TYPE(decltype(*opt), X&);
+ ASSERT_NOEXCEPT(*opt);
+ }
+ {
+ X x{};
+ const optional<const X&> opt{x};
+ ASSERT_SAME_TYPE(decltype(*opt), const X&);
+ ASSERT_NOEXCEPT(*opt);
+ }
+ {
+ static constexpr X x{};
+ constexpr optional<const X&> opt(x);
+ static_assert((*opt).test() == 3, "");
+ }
+#endif
{
constexpr optional<Y> 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<int> opt(0);
static_assert(opt.has_value(), "");
}
+#if TEST_STD_VER >= 26
+ {
+ static constexpr int i = 0;
+ constexpr optional<const int&> 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<X> opt(X{});
assert(opt->test() == 3);
}
+#if TEST_STD_VER >= 26
+ {
+ X x{};
+ std::optional<X&> opt(x);
+ ASSERT_SAME_TYPE(decltype(opt.operator->()), X*);
+ ASSERT_NOEXCEPT(opt.operator->());
+ }
+ {
+ X x{};
+ std::optional<const X&> opt(x);
+ ASSERT_SAME_TYPE(decltype(opt.operator->()), const X*);
+ ASSERT_NOEXCEPT(opt.operator->());
+ }
+ {
+ X x{};
+ optional<X&> opt{x};
+ assert(opt->test() == 3);
+ }
+ {
+ X x{};
+ optional<const X&> 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<Z> opt(Z{});
static_assert(opt->test() == 1, "");
}
+#if TEST_STD_VER >= 26
+ {
+ X x{};
+ const std::optional<X&> opt(x);
+ ASSERT_SAME_TYPE(decltype(opt.operator->()), X*);
+ ASSERT_NOEXCEPT(opt.operator->());
+ }
+ {
+ X x{};
+ const std::optional<const X&> opt(x);
+ ASSERT_SAME_TYPE(decltype(opt.operator->()), const X*);
+ ASSERT_NOEXCEPT(opt.operator->());
+ }
+ {
+ static constexpr Z z{};
+ constexpr optional<const Z&> 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<X&> opt{x};
+ ASSERT_NOT_NOEXCEPT(opt.value());
+ ASSERT_SAME_TYPE(decltype(opt.value()), X&);
+ }
+#endif
#ifndef TEST_HAS_NO_EXCEPTIONS
{
optional<X> 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<int&> 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<X> opt;
assert(opt.value_or({Y(3)}) == 4);
}
-
+#if TEST_STD_VER >= 26
+ {
+ X y{3};
+ const optional<X&> 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 <optional>
+#include "test_macros.h"
+
using std::optional;
struct X
@@ -25,9 +27,14 @@ int main(int, char**)
{
using std::optional;
{
- // expected-error-re at optional:* 2 {{static assertion failed{{.*}}instantiation of optional with a reference type is ill-formed}}
- optional<int&> opt1;
- optional<int&&> opt2;
+#if TEST_STD_VER >= 26
+ // expected-error-re at optional:* {{static assertion failed{{.*}}instantiation of optional with an rvalue reference type is ill-formed}}
+ optional<int&&> opt2;
+#else
+ // expected-error-re at optional:* 2 {{static assertion failed{{.*}}instantiation of optional with a reference type is ill-formed}}
+ optional<int&> opt1;
+ optional<int&&> opt2;
+#endif
}
{
// expected-error-re at optional:* {{static assertion failed{{.*}}instantiation of optional with a non-destructible type is ill-formed}}
>From ffa3d0ca1ee5a7770c0d4c9883bf8a4863860044 Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Sun, 24 Aug 2025 23:29:06 -0400
Subject: [PATCH 04/13] 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<optional<_Tp>&>(*this);
+ auto& __derived_self = static_cast<optional<_Tp>&>(*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<const optional<_Tp>&>(*this);
+ auto& __derived_self = static_cast<const optional<_Tp>&>(*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 fb85642b9c4a9c3db02b1fd5ffbcf127c695f022 Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Mon, 25 Aug 2025 00:03:37 -0400
Subject: [PATCH 05/13] 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 <cassert>
#include <optional>
#include <type_traits>
-#include <cassert>
#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<T>::value, "");
static_assert(std::is_trivially_destructible<optional<T>>::value, "");
}
-
+#endif
return 0;
}
>From a3fee25656fef59dd19106d8e54dd8141e99b897 Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Mon, 25 Aug 2025 00:32:46 -0400
Subject: [PATCH 06/13] 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 16917a3bd9ddd..11d2ce33d876c 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -184,7 +184,8 @@ __cpp_lib_nonmember_container_access 201411L <array> <deque>
__cpp_lib_not_fn 202306L <functional>
201603L // C++17
__cpp_lib_null_iterators 201304L <iterator>
-__cpp_lib_optional 202110L <optional>
+__cpp_lib_optional 202506L <optional>
+ 202110L // C++23
202106L // C++20
201606L // C++17
__cpp_lib_optional_range_support 202406L <optional>
@@ -585,6 +586,8 @@ __cpp_lib_void_t 201411L <type_traits>
# 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 cde2f258b7732..031cf20766279 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
@@ -7433,8 +7433,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 846429ea7ab3d4823a699b0c0bb926eac5d17834 Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Mon, 25 Aug 2025 00:41:20 -0400
Subject: [PATCH 07/13] 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<std::optional<int>>);
static_assert(has_iterator_aliases<std::optional<const int>>);
static_assert(has_iterator_aliases<std::optional<int&>>);
static_assert(has_iterator_aliases<std::optional<const int&>>);
-static_assert(!has_iterator_aliases<std::optional<int(&)[1]>>);
-static_assert(!has_iterator_aliases<std::optional<int(&)()>>);
+static_assert(!has_iterator_aliases<std::optional<int (&)[1]>>);
+static_assert(!has_iterator_aliases<std::optional<int (&)()>>);
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<std::optional<int>, int>);
static_assert(has_value_or<std::optional<int&>, int&>);
static_assert(has_value_or<std::optional<const int&>, const int&>);
-static_assert(!has_value_or<std::optional<int(&)[1]>&&, int(&)[1]>);
-static_assert(!has_value_or<std::optional<int(&)()>&&, int(&)()>);
+static_assert(!has_value_or<std::optional<int (&)[1]>&&, int (&)[1]>);
+static_assert(!has_value_or<std::optional<int (&)()>&&, 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<int>);
}
}
-
// 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<bool>(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 <class T, std::remove_reference_t<T> _Val>
constexpr bool test_ref() {
- using Opt = std::optional<T&>;
- T t{_Val};
- {
- Opt opt;
- auto & v = opt.emplace(t);
- static_assert(std::is_same_v<T&, decltype(v)>, "");
- assert(static_cast<bool>(opt) == true);
- assert(*opt == t);
- assert(&v == &*opt);
- assert(&t == &*opt);
- }
- return true;
+ using Opt = std::optional<T&>;
+ T t{_Val};
+ {
+ Opt opt;
+ auto& v = opt.emplace(t);
+ static_assert(std::is_same_v<T&, decltype(v)>, "");
+ assert(static_cast<bool>(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<double, 15.0>());
assert((test_ref<int, 1>()));
assert((test_ref<double, 15.0>()));
-#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<int&&> opt2; // expected-error-re at optional:* {{static assertion failed{{.*}}instantiation of optional with an rvalue reference type is ill-formed}}
-#else
- std::optional<char &> o1; // expected-error-re at optional:* {{static assertion failed{{.*}}instantiation of optional with a reference type is ill-formed}}
-#endif
- std::optional<NonDestructible> o2; // expected-error-re at optional:* {{static assertion failed{{.*}}instantiation of optional with a non-destructible type is ill-formed}}
- std::optional<char[20]> o3; // expected-error-re at optional:* {{static assertion failed{{.*}}instantiation of optional with an array type is ill-formed}}
- }
-
- {
+ std::optional<int&&>
+ opt2; // expected-error-re at optional:* {{static assertion failed{{.*}}instantiation of optional with an rvalue reference type is ill-formed}}
+#else
+ std::optional<char&>
+ o1; // expected-error-re at optional:* {{static assertion failed{{.*}}instantiation of optional with a reference type is ill-formed}}
+#endif
+ std::optional<NonDestructible>
+ o2; // expected-error-re at optional:* {{static assertion failed{{.*}}instantiation of optional with a non-destructible type is ill-formed}}
+ std::optional<char[20]>
+ o3; // expected-error-re at optional:* {{static assertion failed{{.*}}instantiation of optional with an array type is ill-formed}}
+ }
+
+ {
std::optional< std::in_place_t> o1; // expected-error-re at optional:* {{static assertion failed{{.*}}instantiation of optional with in_place_t is ill-formed}}
std::optional<const std::in_place_t> o2; // expected-error-re at 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 at 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<T>::value, "");
- static_assert(std::is_trivially_destructible<optional<T>>::value, "");
+ typedef int& T;
+ static_assert(std::is_trivially_destructible<T>::value, "");
+ static_assert(std::is_trivially_destructible<optional<T>>::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<bool>(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<X&> opt{x};
- ASSERT_SAME_TYPE(decltype(*opt), X&);
- ASSERT_NOEXCEPT(*opt);
+ X x{};
+ const optional<X&> opt{x};
+ ASSERT_SAME_TYPE(decltype(*opt), X&);
+ ASSERT_NOEXCEPT(*opt);
}
{
- X x{};
- const optional<const X&> opt{x};
- ASSERT_SAME_TYPE(decltype(*opt), const X&);
- ASSERT_NOEXCEPT(*opt);
+ X x{};
+ const optional<const X&> opt{x};
+ ASSERT_SAME_TYPE(decltype(*opt), const X&);
+ ASSERT_NOEXCEPT(*opt);
}
{
- static constexpr X x{};
- constexpr optional<const X&> opt(x);
- static_assert((*opt).test() == 3, "");
+ static constexpr X x{};
+ constexpr optional<const X&> 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<X&> opt;
- assert(opt.value_or(y) == 3);
+ X y{3};
+ const optional<X&> opt;
+ assert(opt.value_or(y) == 3);
}
-#endif
- return 0;
+#endif
+ return 0;
}
>From 5cee3e44be0339e1faf1d669d226e7f9860b966f Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Wed, 27 Aug 2025 00:00:50 -0400
Subject: [PATCH 08/13] 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<optional<_Tp>&>(*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<const optional<_Tp>&>(*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 a5cded6b0804c373c8430fe8dfa16f03cd242656 Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Wed, 27 Aug 2025 18:58:44 -0400
Subject: [PATCH 09/13] 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 f27f4a6738e9d8bb3f3206a67af3f9e6dacd909f Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Wed, 24 Sep 2025 19:04:30 -0400
Subject: [PATCH 10/13] 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 acee825e067c4..2405a38055c6f 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -1005,7 +1005,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<T&>
+ "c++26": 202506, # P2988R12: std::optional<T&>
},
"headers": ["optional"],
},
>From 6638fbcacc823c7dfb2052866eddeb1416acbbcb Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Wed, 24 Sep 2025 19:08:36 -0400
Subject: [PATCH 11/13] 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 <class _Up = remove_cv_t<_Tp> = remove_cv_t<_Tp>>
+ template <class _Up = 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>>))
@@ -962,7 +962,7 @@ public:
return this->has_value() ? this->__get() : static_cast<value_type>(std::forward<_Up>(__v));
}
- template <class _Up = remove_cv_t<_Tp> = remove_cv_t<_Tp>>
+ template <class _Up = remove_cv_t<_Tp>>
# if _LIBCPP_STD_VER >= 26
requires(!is_lvalue_reference_v<_Tp>)
# endif
>From dc7f0973dd7c98d9c4e0edafd2e99ee6c8362c3b Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Wed, 24 Sep 2025 19:21:26 -0400
Subject: [PATCH 12/13] 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<optional<_Tp>&>(*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 <class U> T optional<T>::value_or(U&&);
+#include <concepts>
#include <optional>
template <typename Opt, typename T>
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<const std::in_place_t> o2; // expected-error-re at 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 at optional:* {{static assertion failed{{.*}}instantiation of optional with in_place_t is ill-formed}}
std::optional<const volatile std::in_place_t> o4; // expected-error-re at optional:* {{static assertion failed{{.*}}instantiation of optional with in_place_t is ill-formed}}
- }
+ }
{
std::optional< std::nullopt_t> o1; // expected-error-re at optional:* {{static assertion failed{{.*}}instantiation of optional with nullopt_t is ill-formed}}
>From a5e5e0915810a140ac2e1bf945dcf8adb43f9b91 Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Wed, 24 Sep 2025 23:52:46 -0400
Subject: [PATCH 13/13] 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<class F> constexpr auto and_then(F&&) const&&;
#include <cassert>
+#include <concepts>
#include <optional>
#include "test_macros.h"
@@ -265,9 +266,10 @@ constexpr bool test_ref() {
{
int j = 42;
std::optional<int&> i{j};
- assert(i.and_then(LVal{}) == 1);
+ std::same_as<std::optional<int>> 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<int>);
}
//With & qualifier on F's operator()
@@ -275,10 +277,11 @@ constexpr bool test_ref() {
int j = 42;
std::optional<int&> i{j};
RefQual l{};
- assert(i.and_then(l) == 1);
NORefQual nl{};
+ std::same_as<std::optional<int>> 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<int>);
}
}
@@ -288,9 +291,10 @@ constexpr bool test_ref() {
{
int j = 42;
std::optional<const int&> i{j};
- assert(i.and_then(CLVal{}) == 1);
+ std::same_as<std::optional<int>> 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<int>);
}
//With & qualifier on F's operator()
@@ -298,10 +302,11 @@ constexpr bool test_ref() {
int j = 42;
const std::optional<int&> i{j};
const CRefQual l{};
- assert(i.and_then(l) == 1);
const NOCRefQual nl{};
+ std::same_as<std::optional<int>> 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<int>);
}
}
// Test && overload
@@ -310,9 +315,10 @@ constexpr bool test_ref() {
{
int j = 42;
std::optional<int&> i{j};
- assert(i.and_then(RVRefQual{}) == 1);
+ std::same_as<std::optional<int>> 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<int>);
}
}
@@ -323,10 +329,11 @@ constexpr bool test_ref() {
int j = 42;
const std::optional<int&> i{j};
const RVCRefQual l{};
- assert(i.and_then(std::move(l)) == 1);
const NORVCRefQual nl{};
+ std::same_as<std::optional<int>> 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<int>);
}
}
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 <cassert>
+#include <concepts>
#include <optional>
#include <type_traits>
#include <utility>
@@ -201,20 +202,21 @@ constexpr bool test() {
return true;
}
+#if TEST_STD_VER >= 26
constexpr bool test_ref() {
{
std::optional<int&> opt1;
- auto opt1r = opt1.transform([](int i) { return i + 2; });
+ std::same_as<std::optional<int>> decltype(auto) opt1r = opt1.transform([](int i) { return i + 2; });
assert(!opt1);
- ASSERT_SAME_TYPE(decltype(opt1r), std::optional<int>);
+ assert(!opt1r);
}
{
int i = 42;
std::optional<int&> opt{i};
- auto o2 = opt.transform([](int j) { return j + 2; });
+ std::same_as<std::optional<int>> decltype(auto) o2 = opt.transform([](int i) { return i + 2; });
+
assert(*o2 == 44);
- ASSERT_SAME_TYPE(decltype(o2), std::optional<int>);
}
// Test & overload
{
@@ -222,9 +224,9 @@ constexpr bool test_ref() {
{
int i = 42;
std::optional<int&> opt{i};
- auto o3 = opt.transform(LVal{});
+ std::same_as<std::optional<int>> decltype(auto) o3 = opt.transform(LVal{});
+
assert(*o3 == 1);
- ASSERT_SAME_TYPE(decltype(o3), std::optional<int>);
}
//With & qualifier on F's operator()
@@ -232,9 +234,9 @@ constexpr bool test_ref() {
int i = 42;
std::optional<int&> opt{i};
RefQual l{};
- auto o3 = opt.transform(l);
+ std::same_as<std::optional<int>> decltype(auto) o3 = opt.transform(l);
+
assert(*o3 == 1);
- ASSERT_SAME_TYPE(decltype(o3), std::optional<int>);
}
}
// const& overload
@@ -243,9 +245,9 @@ constexpr bool test_ref() {
{
int i = 42;
std::optional<const int&> opt{i};
- auto o3 = std::as_const(opt).transform(CLVal{});
+ std::same_as<std::optional<int>> decltype(auto) o3 = std::as_const(opt).transform(CLVal{});
+
assert(*o3 == 1);
- ASSERT_SAME_TYPE(decltype(o3), std::optional<int>);
}
//With & qualifier on F's operator()
@@ -253,9 +255,9 @@ constexpr bool test_ref() {
int i = 42;
const std::optional<int&> opt{i};
const CRefQual l{};
- auto o3 = opt.transform(l);
+ std::same_as<std::optional<int>> decltype(auto) o3 = opt.transform(l);
+
assert(*o3 == 1);
- ASSERT_SAME_TYPE(decltype(o3), std::optional<int>);
}
}
@@ -265,18 +267,17 @@ constexpr bool test_ref() {
{
int i = 42;
std::optional<int> opt{i};
- auto o3 = std::move(opt).transform(RVal{});
+ std::same_as<std::optional<int>> decltype(auto) o3 = std::move(opt).transform(RVal{});
+
assert(*o3 == 1);
- ASSERT_SAME_TYPE(decltype(o3), std::optional<int>);
}
//With & qualifier on F's operator()
{
int i = 42;
std::optional<int&> opt{i};
- auto o3 = std::move(opt).transform(RVRefQual{});
+ std::same_as<std::optional<int>> decltype(auto) o3 = std::move(opt).transform(RVRefQual{});
assert(*o3 == 1);
- ASSERT_SAME_TYPE(decltype(o3), std::optional<int>);
}
}
@@ -287,9 +288,8 @@ constexpr bool test_ref() {
int i = 42;
std::optional<int&> opt{i};
const RVCRefQual rvc{};
- auto o3 = opt.transform(std::move(rvc));
+ std::same_as<std::optional<int>> decltype(auto) o3 = opt.transform(std::move(rvc));
assert(*o3 == 1);
- ASSERT_SAME_TYPE(decltype(o3), std::optional<int>);
}
}
{
@@ -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;
}
More information about the libcxx-commits
mailing list