[libcxx-commits] [libcxx] [libc++] Implement P3168R2: Give optional range support (PR #149441)
William Tran-Viet via libcxx-commits
libcxx-commits at lists.llvm.org
Sat Jul 19 09:06:39 PDT 2025
https://github.com/smallp-o-p updated https://github.com/llvm/llvm-project/pull/149441
>From 6609f9e1a0b6a2c2e44574bc28841a0c35288827 Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Mon, 14 Jul 2025 23:13:03 -0400
Subject: [PATCH 1/7] Implement P2988R12: Give optional range support
---
libcxx/docs/FeatureTestMacroTable.rst | 2 +-
libcxx/docs/ReleaseNotes/21.rst | 2 +-
libcxx/docs/Status/Cxx2cPapers.csv | 2 +-
libcxx/include/__iterator/wrap_iter.h | 2 +
libcxx/include/optional | 49 +++++++
libcxx/include/version | 2 +-
libcxx/modules/std/optional.inc | 11 +-
.../test/libcxx/transitive_includes/cxx26.csv | 47 +++++++
.../optional.version.compile.pass.cpp | 16 +--
.../version.version.compile.pass.cpp | 16 +--
.../std/utilities/optional/iterator.pass.cpp | 124 ++++++++++++++++++
.../generate_feature_test_macro_components.py | 1 -
12 files changed, 246 insertions(+), 28 deletions(-)
create mode 100644 libcxx/test/std/utilities/optional/iterator.pass.cpp
diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 61805726a4ff0..5114ae59d7c67 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -480,7 +480,7 @@ Status
---------------------------------------------------------- -----------------
``__cpp_lib_not_fn`` ``202306L``
---------------------------------------------------------- -----------------
- ``__cpp_lib_optional_range_support`` *unimplemented*
+ ``__cpp_lib_optional_range_support`` ``202406L``
---------------------------------------------------------- -----------------
``__cpp_lib_out_ptr`` ``202311L``
---------------------------------------------------------- -----------------
diff --git a/libcxx/docs/ReleaseNotes/21.rst b/libcxx/docs/ReleaseNotes/21.rst
index d31ca0130cb80..e2ff3881ea544 100644
--- a/libcxx/docs/ReleaseNotes/21.rst
+++ b/libcxx/docs/ReleaseNotes/21.rst
@@ -53,7 +53,7 @@ Implemented Papers
- P2711R1: Making multi-param constructors of ``views`` ``explicit`` (`Github <https://github.com/llvm/llvm-project/issues/105252>`__)
- P2770R0: Stashing stashing ``iterators`` for proper flattening (`Github <https://github.com/llvm/llvm-project/issues/105250>`__)
- P2655R3: ``common_reference_t`` of ``reference_wrapper`` Should Be a Reference Type (`Github <https://github.com/llvm/llvm-project/issues/105260>`__)
-
+- 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 febb0c176f9c4..95f0d08fdde1c 100644
--- a/libcxx/docs/Status/Cxx2cPapers.csv
+++ b/libcxx/docs/Status/Cxx2cPapers.csv
@@ -66,7 +66,7 @@
"`P2747R2 <https://wg21.link/P2747R2>`__","``constexpr`` placement new","2024-06 (St. Louis)","|Complete|","20",""
"`P2997R1 <https://wg21.link/P2997R1>`__","Removing the common reference requirement from the indirectly invocable concepts","2024-06 (St. Louis)","|Complete|","19","Implemented as a DR against C++20. (MSVC STL and libstdc++ will do the same.)"
"`P2389R2 <https://wg21.link/P2389R2>`__","``dextents`` Index Type Parameter","2024-06 (St. Louis)","|Complete|","19",""
-"`P3168R2 <https://wg21.link/P3168R2>`__","Give ``std::optional`` Range Support","2024-06 (St. Louis)","","",""
+"`P3168R2 <https://wg21.link/P3168R2>`__","Give ``std::optional`` Range Support","2024-06 (St. Louis)","","21","|Complete|"
"`P3217R0 <https://wg21.link/P3217R0>`__","Adjoints to 'Enabling list-initialization for algorithms': find_last","2024-06 (St. Louis)","","",""
"`P2985R0 <https://wg21.link/P2985R0>`__","A type trait for detecting virtual base classes","2024-06 (St. Louis)","|Complete|","20",""
"`P0843R14 <https://wg21.link/P0843R14>`__","``inplace_vector``","2024-06 (St. Louis)","","",""
diff --git a/libcxx/include/__iterator/wrap_iter.h b/libcxx/include/__iterator/wrap_iter.h
index 2b5bc489dd44c..7610586ddecbb 100644
--- a/libcxx/include/__iterator/wrap_iter.h
+++ b/libcxx/include/__iterator/wrap_iter.h
@@ -117,6 +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 _Iter1>
diff --git a/libcxx/include/optional b/libcxx/include/optional
index e81bff50daad6..1292d969fc9ed 100644
--- a/libcxx/include/optional
+++ b/libcxx/include/optional
@@ -20,6 +20,11 @@ namespace std {
template <class T>
class optional;
+ template<class T>
+ constexpr bool ranges::enable_view<optional<T>> = true;
+ template<class T>
+ constexpr auto format_kind<optional<T>> = range_format::disabled;
+
template<class T>
concept is-derived-from-optional = requires(const T& t) { // exposition only
[]<class U>(const optional<U>&){ }(t);
@@ -102,6 +107,8 @@ namespace std {
class optional {
public:
using value_type = T;
+ using iterator = implementation-defined; // see [optional.iterators]
+ using const_iterator = implementation-defined; // see [optional.iterators]
// [optional.ctor], constructors
constexpr optional() noexcept;
@@ -135,6 +142,12 @@ namespace std {
// [optional.swap], swap
void swap(optional &) noexcept(see below ); // constexpr in C++20
+ // [optional.iterators], iterator support
+ constexpr iterator begin() noexcept;
+ constexpr const_iterator begin() const noexcept;
+ constexpr iterator end() noexcept;
+ constexpr const_iterator end() const noexcept;
+
// [optional.observe], observers
constexpr T const *operator->() const noexcept;
constexpr T *operator->() noexcept;
@@ -233,6 +246,13 @@ namespace std {
# include <initializer_list>
# include <version>
+# if _LIBCPP_STD_VER >= 26
+# include <__cstddef/ptrdiff_t.h>
+# include <__format/range_format.h>
+# include <__iterator/wrap_iter.h>
+# include <__ranges/enable_view.h>
+# endif
+
// standard-mandated includes
// [optional.syn]
@@ -567,6 +587,14 @@ using __optional_sfinae_assign_base_t _LIBCPP_NODEBUG =
template <class _Tp>
class optional;
+# if _LIBCPP_STD_VER >= 26
+template <class _Tp>
+constexpr bool ranges::enable_view<optional<_Tp>> = true;
+
+template <class _Tp>
+constexpr range_format format_kind<optional<_Tp>> = range_format::disabled;
+# endif
+
# if _LIBCPP_STD_VER >= 20
template <class _Tp>
@@ -586,8 +614,17 @@ class _LIBCPP_DECLSPEC_EMPTY_BASES optional
private __optional_sfinae_assign_base_t<_Tp> {
using __base _LIBCPP_NODEBUG = __optional_move_assign_base<_Tp>;
+# if _LIBCPP_STD_VER >= 26
+ using pointer = std::add_pointer_t<std::remove_cvref_t<_Tp>>;
+ using const_pointer = std::add_pointer_t<const std::remove_cvref_t<_Tp>>;
+# endif
+
public:
using value_type = _Tp;
+# if _LIBCPP_STD_VER >= 26
+ using iterator = __wrap_iter<pointer>;
+ using const_iterator = __wrap_iter<const_pointer>;
+# endif
using __trivially_relocatable _LIBCPP_NODEBUG =
conditional_t<__libcpp_is_trivially_relocatable<_Tp>::value, optional, void>;
@@ -792,6 +829,18 @@ public:
}
}
+# if _LIBCPP_STD_VER >= 26
+ // [optional.iterators], iterator support
+ _LIBCPP_HIDE_FROM_ABI constexpr iterator begin() noexcept { return iterator(std::addressof(this->__get())); }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr const_iterator begin() const noexcept {
+ return const_iterator(std::addressof(this->__get()));
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr iterator end() noexcept { return begin() + this->has_value(); }
+ _LIBCPP_HIDE_FROM_ABI constexpr const_iterator end() const noexcept { return begin() + this->has_value(); }
+# 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());
diff --git a/libcxx/include/version b/libcxx/include/version
index d98049bd57046..88bceb9f5dc01 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -585,7 +585,7 @@ __cpp_lib_void_t 201411L <type_traits>
# define __cpp_lib_mdspan 202406L
# undef __cpp_lib_not_fn
# define __cpp_lib_not_fn 202306L
-// # define __cpp_lib_optional_range_support 202406L
+# define __cpp_lib_optional_range_support 202406L
# undef __cpp_lib_out_ptr
# define __cpp_lib_out_ptr 202311L
// # define __cpp_lib_philox_engine 202406L
diff --git a/libcxx/modules/std/optional.inc b/libcxx/modules/std/optional.inc
index 0f812bc0e24a4..9ee51117277ce 100644
--- a/libcxx/modules/std/optional.inc
+++ b/libcxx/modules/std/optional.inc
@@ -10,7 +10,12 @@
export namespace std {
// [optional.optional], class template optional
using std::optional;
-
+#if _LIBCPP_STD_VER >= 26
+ // [optional.iterators], iterator support
+ namespace ranges {
+ using std::ranges::enable_view;
+ }
+#endif
// [optional.nullopt], no-value state indicator
using std::nullopt;
using std::nullopt_t;
@@ -18,6 +23,10 @@ export namespace std {
// [optional.bad.access], class bad_optional_access
using std::bad_optional_access;
+#if _LIBCPP_STD_VER >= 26
+ using std::format_kind;
+#endif
+
// [optional.relops], relational operators
using std::operator==;
using std::operator!=;
diff --git a/libcxx/test/libcxx/transitive_includes/cxx26.csv b/libcxx/test/libcxx/transitive_includes/cxx26.csv
index 5f906338f4b7c..7d383d5c8e819 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx26.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx26.csv
@@ -2,6 +2,7 @@ algorithm cctype
algorithm climits
algorithm compare
algorithm cstdint
+algorithm cstdio
algorithm cstring
algorithm ctime
algorithm cwchar
@@ -11,6 +12,8 @@ algorithm iosfwd
algorithm limits
algorithm optional
algorithm ratio
+algorithm stdexcept
+algorithm string_view
algorithm tuple
algorithm version
any cstdint
@@ -330,26 +333,32 @@ flat_map cctype
flat_map climits
flat_map compare
flat_map cstdint
+flat_map cstdio
flat_map cstring
flat_map cwchar
flat_map cwctype
flat_map initializer_list
+flat_map iosfwd
flat_map limits
flat_map optional
flat_map stdexcept
+flat_map string_view
flat_map tuple
flat_map version
flat_set cctype
flat_set climits
flat_set compare
flat_set cstdint
+flat_set cstdio
flat_set cstring
flat_set cwchar
flat_set cwctype
flat_set initializer_list
+flat_set iosfwd
flat_set limits
flat_set optional
flat_set stdexcept
+flat_set string_view
flat_set tuple
flat_set version
format array
@@ -417,13 +426,16 @@ functional array
functional cctype
functional compare
functional cstdint
+functional cstdio
functional cstring
functional cwchar
functional cwctype
functional initializer_list
+functional iosfwd
functional limits
functional optional
functional stdexcept
+functional string_view
functional tuple
functional typeinfo
functional unordered_map
@@ -623,13 +635,16 @@ locale version
map cctype
map compare
map cstdint
+map cstdio
map cstring
map cwchar
map cwctype
map initializer_list
+map iosfwd
map limits
map optional
map stdexcept
+map string_view
map tuple
map version
mdspan array
@@ -673,22 +688,36 @@ mutex typeinfo
mutex version
new version
numbers version
+numeric cctype
numeric climits
numeric compare
numeric cstdint
+numeric cstdio
numeric cstring
numeric ctime
+numeric cwchar
+numeric cwctype
numeric initializer_list
+numeric iosfwd
numeric limits
numeric optional
numeric ratio
+numeric stdexcept
+numeric string_view
numeric tuple
numeric version
+optional cctype
optional compare
optional cstdint
+optional cstdio
optional cstring
+optional cwchar
+optional cwctype
optional initializer_list
+optional iosfwd
optional limits
+optional stdexcept
+optional string_view
optional version
ostream array
ostream bitset
@@ -806,6 +835,7 @@ ranges limits
ranges optional
ranges span
ranges stdexcept
+ranges string_view
ranges tuple
ranges variant
ranges version
@@ -851,12 +881,16 @@ semaphore version
set cctype
set compare
set cstdint
+set cstdio
set cstring
set cwchar
set cwctype
set initializer_list
+set iosfwd
set limits
set optional
+set stdexcept
+set string_view
set tuple
set version
shared_mutex cerrno
@@ -1091,21 +1125,34 @@ typeindex typeinfo
typeindex version
typeinfo cstdint
typeinfo version
+unordered_map cctype
unordered_map compare
unordered_map cstdint
+unordered_map cstdio
unordered_map cstring
+unordered_map cwchar
+unordered_map cwctype
unordered_map initializer_list
+unordered_map iosfwd
unordered_map limits
unordered_map optional
unordered_map stdexcept
+unordered_map string_view
unordered_map tuple
unordered_map version
+unordered_set cctype
unordered_set compare
unordered_set cstdint
+unordered_set cstdio
unordered_set cstring
+unordered_set cwchar
+unordered_set cwctype
unordered_set initializer_list
+unordered_set iosfwd
unordered_set limits
unordered_set optional
+unordered_set stdexcept
+unordered_set string_view
unordered_set tuple
unordered_set version
utility compare
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 148a6dbc0d3e4..f5a05ee300dd7 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
@@ -152,17 +152,11 @@
# error "__cpp_lib_optional should have the value 202110L in c++26"
# endif
-# if !defined(_LIBCPP_VERSION)
-# ifndef __cpp_lib_optional_range_support
-# error "__cpp_lib_optional_range_support should be defined in c++26"
-# endif
-# if __cpp_lib_optional_range_support != 202406L
-# error "__cpp_lib_optional_range_support should have the value 202406L in c++26"
-# endif
-# else
-# ifdef __cpp_lib_optional_range_support
-# error "__cpp_lib_optional_range_support should not be defined because it is unimplemented in libc++!"
-# endif
+# ifndef __cpp_lib_optional_range_support
+# error "__cpp_lib_optional_range_support should be defined in c++26"
+# endif
+# if __cpp_lib_optional_range_support != 202406L
+# error "__cpp_lib_optional_range_support should have the value 202406L in c++26"
# endif
#endif // TEST_STD_VER > 23
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 962688e06188a..ba445248a711f 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
@@ -7443,17 +7443,11 @@
# error "__cpp_lib_optional should have the value 202110L in c++26"
# endif
-# if !defined(_LIBCPP_VERSION)
-# ifndef __cpp_lib_optional_range_support
-# error "__cpp_lib_optional_range_support should be defined in c++26"
-# endif
-# if __cpp_lib_optional_range_support != 202406L
-# error "__cpp_lib_optional_range_support should have the value 202406L in c++26"
-# endif
-# else
-# ifdef __cpp_lib_optional_range_support
-# error "__cpp_lib_optional_range_support should not be defined because it is unimplemented in libc++!"
-# endif
+# ifndef __cpp_lib_optional_range_support
+# error "__cpp_lib_optional_range_support should be defined in c++26"
+# endif
+# if __cpp_lib_optional_range_support != 202406L
+# error "__cpp_lib_optional_range_support should have the value 202406L in c++26"
# endif
# ifndef __cpp_lib_out_ptr
diff --git a/libcxx/test/std/utilities/optional/iterator.pass.cpp b/libcxx/test/std/utilities/optional/iterator.pass.cpp
new file mode 100644
index 0000000000000..d9add15128ece
--- /dev/null
+++ b/libcxx/test/std/utilities/optional/iterator.pass.cpp
@@ -0,0 +1,124 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 T> class optional::iterator;
+
+// 1: optional::iterator and optional const_iterator satisfy contiguous_iterator and random_access_iterator
+// 2. The value types and reference types for optional::iterator and optional::const_iterator are {_Tp, _Tp&} and {_Tp, const _Tp&} respectively
+// 3: The optional::begin() and optional::end() are marked noexcept.
+// 4: optionals that have a value have begin() != end(), whereas one that doesn't has begin() == end();
+// 5: The corresponding size for the following optionals is respected: has_value() == 1, !has_value() == 0
+// 6: Dereferencing an engaged optional's iterator returns the correct value.
+// 7: std::ranges::enable_view<optional<T>> == true, and std::format_kind<optional<T>> == true
+// 8: Verify that an iterator for loop counts only 1 item for an engaged optional, and 0 for an unegaged one.
+// 9: 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.
+
+#include <cassert>
+#include <optional>
+
+#include "test_macros.h"
+#include "check_assertion.h"
+
+constexpr int test_loop(const std::optional<char> val) {
+ int size = 0;
+ for (auto&& v : val) {
+ std::ignore = v;
+ size++;
+ }
+ return size;
+}
+
+constexpr bool test_reset() {
+ std::optional<int> val{1};
+ assert(val.begin() != val.end());
+ val.reset();
+ assert(val.begin() == val.end());
+ val = 1;
+ assert(val.begin() != val.end());
+ assert(*(val.begin()) == 1);
+ return true;
+}
+
+int main(int, char**) {
+ constexpr const std::optional<char> opt{'a'};
+ constexpr std::optional<char> unengaged_opt;
+ std::optional<char> nonconst_opt{'n'};
+
+ // 1
+ {
+ static_assert(std::contiguous_iterator<decltype(nonconst_opt.begin())>);
+ static_assert(std::contiguous_iterator<decltype(nonconst_opt.end())>);
+ static_assert(std::random_access_iterator<decltype(nonconst_opt.begin())>);
+ static_assert(std::random_access_iterator<decltype(nonconst_opt.end())>);
+
+ static_assert(std::contiguous_iterator<decltype(opt.begin())>);
+ static_assert(std::contiguous_iterator<decltype(opt.end())>);
+ static_assert(std::random_access_iterator<decltype(opt.begin())>);
+ static_assert(std::random_access_iterator<decltype(opt.end())>);
+ }
+
+ { // 2
+ static_assert(std::same_as<typename decltype(opt.begin())::value_type, char>);
+ static_assert(std::same_as<typename decltype(opt.begin())::reference, const char&>);
+ static_assert(std::same_as<typename decltype(nonconst_opt.begin())::value_type, char>);
+ static_assert(std::same_as<typename decltype(nonconst_opt.begin())::reference, char&>);
+ }
+
+ // 3
+ {
+ ASSERT_NOEXCEPT(opt.begin());
+ ASSERT_NOEXCEPT(opt.end());
+ ASSERT_NOEXCEPT(nonconst_opt.begin());
+ ASSERT_NOEXCEPT(nonconst_opt.end());
+ }
+
+ { // 4
+ static_assert(opt.begin() != opt.end());
+ static_assert(unengaged_opt.begin() == unengaged_opt.end());
+ assert(unengaged_opt.begin() == unengaged_opt.end());
+ assert(opt.begin() != opt.end());
+ assert(nonconst_opt.begin() != opt.end());
+ }
+
+ // 5
+ {
+ static_assert(std::ranges::size(opt) == 1);
+ static_assert(std::ranges::size(unengaged_opt) == 0);
+ assert(std::ranges::size(opt) == 1);
+ assert(std::ranges::size(unengaged_opt) == 0);
+ }
+
+ { // 6
+ static_assert(*opt.begin() == 'a');
+ assert(*(opt.begin()) == 'a');
+ assert(*(nonconst_opt.begin()) == 'n');
+ }
+
+ { // 7
+ static_assert(std::ranges::enable_view<std::optional<char>> == true);
+ assert(std::format_kind<std::optional<char>> == std::range_format::disabled);
+ }
+
+ { // 8
+ static_assert(test_loop(opt) == 1);
+ static_assert(test_loop(unengaged_opt) == 0);
+ assert(test_loop(opt) == 1);
+ assert(test_loop(unengaged_opt) == 0);
+ }
+
+ { // 9
+ static_assert(test_reset());
+ assert(test_reset());
+ }
+
+ return 0;
+}
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index fe175fd758726..152adfbbc52da 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -1014,7 +1014,6 @@ def add_version_header(tc):
"name": "__cpp_lib_optional_range_support",
"values": {"c++26": 202406}, # P3168R2 Give std::optional Range Support
"headers": ["optional"],
- "unimplemented": True,
},
{
"name": "__cpp_lib_out_ptr",
>From d2a4080d6574a46052b40060f180bd2d84e3e0a6 Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Thu, 17 Jul 2025 23:01:40 -0400
Subject: [PATCH 2/7] Misinputted columns in Cxx2cPapers.csv
---
libcxx/docs/Status/Cxx2cPapers.csv | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libcxx/docs/Status/Cxx2cPapers.csv b/libcxx/docs/Status/Cxx2cPapers.csv
index 95f0d08fdde1c..3fc2a99b1671d 100644
--- a/libcxx/docs/Status/Cxx2cPapers.csv
+++ b/libcxx/docs/Status/Cxx2cPapers.csv
@@ -66,7 +66,7 @@
"`P2747R2 <https://wg21.link/P2747R2>`__","``constexpr`` placement new","2024-06 (St. Louis)","|Complete|","20",""
"`P2997R1 <https://wg21.link/P2997R1>`__","Removing the common reference requirement from the indirectly invocable concepts","2024-06 (St. Louis)","|Complete|","19","Implemented as a DR against C++20. (MSVC STL and libstdc++ will do the same.)"
"`P2389R2 <https://wg21.link/P2389R2>`__","``dextents`` Index Type Parameter","2024-06 (St. Louis)","|Complete|","19",""
-"`P3168R2 <https://wg21.link/P3168R2>`__","Give ``std::optional`` Range Support","2024-06 (St. Louis)","","21","|Complete|"
+"`P3168R2 <https://wg21.link/P3168R2>`__","Give ``std::optional`` Range Support","2024-06 (St. Louis)","|Complete|","21",""
"`P3217R0 <https://wg21.link/P3217R0>`__","Adjoints to 'Enabling list-initialization for algorithms': find_last","2024-06 (St. Louis)","","",""
"`P2985R0 <https://wg21.link/P2985R0>`__","A type trait for detecting virtual base classes","2024-06 (St. Louis)","|Complete|","20",""
"`P0843R14 <https://wg21.link/P0843R14>`__","``inplace_vector``","2024-06 (St. Louis)","","",""
>From c1291d0477b3287530db72a11bc449e23f10bbf7 Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Fri, 18 Jul 2025 00:34:27 -0400
Subject: [PATCH 3/7] Address most comments
---
libcxx/docs/ReleaseNotes/21.rst | 1 +
libcxx/include/optional | 11 +-
.../std/utilities/optional/iterator.pass.cpp | 178 +++++++++++-------
3 files changed, 115 insertions(+), 75 deletions(-)
diff --git a/libcxx/docs/ReleaseNotes/21.rst b/libcxx/docs/ReleaseNotes/21.rst
index e2ff3881ea544..e07f825fd19cc 100644
--- a/libcxx/docs/ReleaseNotes/21.rst
+++ b/libcxx/docs/ReleaseNotes/21.rst
@@ -54,6 +54,7 @@ Implemented Papers
- P2770R0: Stashing stashing ``iterators`` for proper flattening (`Github <https://github.com/llvm/llvm-project/issues/105250>`__)
- P2655R3: ``common_reference_t`` of ``reference_wrapper`` Should Be a Reference Type (`Github <https://github.com/llvm/llvm-project/issues/105260>`__)
- P3168R2 Give ``std::optional`` Range Support (`Github <https://github.com/llvm/llvm-project/issues/105430>`__)
+
Improvements and New Features
-----------------------------
diff --git a/libcxx/include/optional b/libcxx/include/optional
index 1292d969fc9ed..caa21000103de 100644
--- a/libcxx/include/optional
+++ b/libcxx/include/optional
@@ -199,13 +199,17 @@ namespace std {
# include <__compare/three_way_comparable.h>
# include <__concepts/invocable.h>
# include <__config>
+# include <__cstddef/ptrdiff_t.h>
# include <__exception/exception.h>
+# include <__format/range_format.h>
# include <__functional/hash.h>
# include <__functional/invoke.h>
# include <__functional/unary_function.h>
# include <__fwd/functional.h>
+# include <__iterator/wrap_iter.h>
# include <__memory/addressof.h>
# include <__memory/construct_at.h>
+# include <__ranges/enable_view.h>
# include <__tuple/sfinae_helpers.h>
# include <__type_traits/add_pointer.h>
# include <__type_traits/conditional.h>
@@ -246,13 +250,6 @@ namespace std {
# include <initializer_list>
# include <version>
-# if _LIBCPP_STD_VER >= 26
-# include <__cstddef/ptrdiff_t.h>
-# include <__format/range_format.h>
-# include <__iterator/wrap_iter.h>
-# include <__ranges/enable_view.h>
-# endif
-
// standard-mandated includes
// [optional.syn]
diff --git a/libcxx/test/std/utilities/optional/iterator.pass.cpp b/libcxx/test/std/utilities/optional/iterator.pass.cpp
index d9add15128ece..3bb831d9dfeb9 100644
--- a/libcxx/test/std/utilities/optional/iterator.pass.cpp
+++ b/libcxx/test/std/utilities/optional/iterator.pass.cpp
@@ -11,30 +11,94 @@
// <optional>
// template <class T> class optional::iterator;
-
-// 1: optional::iterator and optional const_iterator satisfy contiguous_iterator and random_access_iterator
-// 2. The value types and reference types for optional::iterator and optional::const_iterator are {_Tp, _Tp&} and {_Tp, const _Tp&} respectively
-// 3: The optional::begin() and optional::end() are marked noexcept.
-// 4: optionals that have a value have begin() != end(), whereas one that doesn't has begin() == end();
-// 5: The corresponding size for the following optionals is respected: has_value() == 1, !has_value() == 0
-// 6: Dereferencing an engaged optional's iterator returns the correct value.
-// 7: std::ranges::enable_view<optional<T>> == true, and std::format_kind<optional<T>> == true
-// 8: Verify that an iterator for loop counts only 1 item for an engaged optional, and 0 for an unegaged one.
-// 9: 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.
+// template <class T> class optional::const_iterator;
+// constexpr iterator optional::begin() noexcept;
+// constexpr iterator optional::end() noexcept;
+// constexpr const_iterator optional::begin() noexcept;
+// constexpr const_iterator optional::end() noexcept;
#include <cassert>
+#include <concepts>
+#include <iterator>
#include <optional>
+#include <ranges>
+#include <type_traits>
#include "test_macros.h"
#include "check_assertion.h"
-constexpr int test_loop(const std::optional<char> val) {
- int size = 0;
- for (auto&& v : val) {
- std::ignore = v;
- size++;
- }
- return size;
+constexpr bool test_concepts() {
+ const std::optional<char> opt{'a'};
+ std::optional<char> nonconst_opt{'n'};
+
+ assert(std::contiguous_iterator<decltype(nonconst_opt.begin())>);
+ assert(std::contiguous_iterator<decltype(nonconst_opt.end())>);
+ assert(std::random_access_iterator<decltype(nonconst_opt.begin())>);
+ assert(std::random_access_iterator<decltype(nonconst_opt.end())>);
+
+ assert(std::contiguous_iterator<decltype(opt.begin())>);
+ assert(std::contiguous_iterator<decltype(opt.end())>);
+ assert(std::random_access_iterator<decltype(opt.begin())>);
+ assert(std::random_access_iterator<decltype(opt.end())>);
+
+ return true;
+}
+
+constexpr bool test_types() {
+ const std::optional<char> opt{'a'};
+ std::optional<char> nonconst_opt{'n'};
+
+ assert((std::is_same_v<typename decltype(opt.begin())::value_type, char>));
+ assert((std::is_same_v<typename decltype(opt.begin())::reference, const char&>));
+ assert((std::is_same_v<typename decltype(nonconst_opt.begin())::value_type, char>));
+ assert((std::is_same_v<typename decltype(nonconst_opt.begin())::reference, char&>));
+ return true;
+}
+
+constexpr bool test_size() {
+ const std::optional<char> opt{'a'};
+ std::optional<char> unengaged_opt;
+
+ assert(std::ranges::size(opt) == 1);
+ assert(std::ranges::size(unengaged_opt) == 0);
+
+ return true;
+}
+
+constexpr bool test_begin_end() {
+ const std::optional<char> opt{'a'};
+ std::optional<char> unengaged_opt;
+
+ assert(opt.begin() != opt.end());
+ assert(unengaged_opt.begin() == unengaged_opt.end());
+ return true;
+}
+
+constexpr bool test_noexcept() {
+ const std::optional<char> opt{'a'};
+ std::optional<char> nonconst_opt{'n'};
+
+ ASSERT_NOEXCEPT(opt.begin());
+ ASSERT_NOEXCEPT(opt.end());
+ ASSERT_NOEXCEPT(nonconst_opt.begin());
+ ASSERT_NOEXCEPT(nonconst_opt.end());
+
+ return true;
+}
+
+constexpr bool test_value() {
+ const std::optional<char> opt{'a'};
+ std::optional<char> nonconst_opt{'n'};
+
+ assert(*(opt.begin()) == 'a');
+ assert(*(nonconst_opt.begin()) == 'n');
+ return true;
+}
+
+constexpr bool test_syn() {
+ assert(std::ranges::enable_view<std::optional<char>> == true);
+ assert(std::format_kind<std::optional<char>> == std::range_format::disabled);
+ return true;
}
constexpr bool test_reset() {
@@ -48,76 +112,54 @@ constexpr bool test_reset() {
return true;
}
-int main(int, char**) {
- constexpr const std::optional<char> opt{'a'};
- constexpr std::optional<char> unengaged_opt;
- std::optional<char> nonconst_opt{'n'};
- // 1
+int main(int, char**) {
+ // 1: optional::iterator and optional const_iterator satisfy contiguous_iterator and random_access_iterator
{
- static_assert(std::contiguous_iterator<decltype(nonconst_opt.begin())>);
- static_assert(std::contiguous_iterator<decltype(nonconst_opt.end())>);
- static_assert(std::random_access_iterator<decltype(nonconst_opt.begin())>);
- static_assert(std::random_access_iterator<decltype(nonconst_opt.end())>);
-
- static_assert(std::contiguous_iterator<decltype(opt.begin())>);
- static_assert(std::contiguous_iterator<decltype(opt.end())>);
- static_assert(std::random_access_iterator<decltype(opt.begin())>);
- static_assert(std::random_access_iterator<decltype(opt.end())>);
- }
-
- { // 2
- static_assert(std::same_as<typename decltype(opt.begin())::value_type, char>);
- static_assert(std::same_as<typename decltype(opt.begin())::reference, const char&>);
- static_assert(std::same_as<typename decltype(nonconst_opt.begin())::value_type, char>);
- static_assert(std::same_as<typename decltype(nonconst_opt.begin())::reference, char&>);
+ test_concepts();
+ static_assert(test_concepts());
}
- // 3
+ // 2: The value types and reference types for optional::iterator and optional::const_iterator are {_Tp, _Tp&} and {_Tp, const _Tp&} respectively
{
- ASSERT_NOEXCEPT(opt.begin());
- ASSERT_NOEXCEPT(opt.end());
- ASSERT_NOEXCEPT(nonconst_opt.begin());
- ASSERT_NOEXCEPT(nonconst_opt.end());
+ test_types();
+ static_assert(test_types());
}
- { // 4
- static_assert(opt.begin() != opt.end());
- static_assert(unengaged_opt.begin() == unengaged_opt.end());
- assert(unengaged_opt.begin() == unengaged_opt.end());
- assert(opt.begin() != opt.end());
- assert(nonconst_opt.begin() != opt.end());
+ // 3: The optional::begin() and optional::end() are marked noexcept.
+ {
+ test_noexcept();
+ static_assert(test_noexcept());
}
- // 5
+ // 4: optionals that have a value have begin() != end(), whereas one that doesn't has begin() == end();
{
- static_assert(std::ranges::size(opt) == 1);
- static_assert(std::ranges::size(unengaged_opt) == 0);
- assert(std::ranges::size(opt) == 1);
- assert(std::ranges::size(unengaged_opt) == 0);
+ test_begin_end();
+ static_assert(test_begin_end());
}
- { // 6
- static_assert(*opt.begin() == 'a');
- assert(*(opt.begin()) == 'a');
- assert(*(nonconst_opt.begin()) == 'n');
+ // 5: The corresponding size for the following optionals is respected: has_value() == 1, !has_value() == 0
+ {
+ test_size();
+ static_assert(test_size());
}
- { // 7
- static_assert(std::ranges::enable_view<std::optional<char>> == true);
- assert(std::format_kind<std::optional<char>> == std::range_format::disabled);
+ // 6: Dereferencing an engaged optional's iterator returns the correct value.
+ {
+ test_value();
+ static_assert(test_value());
}
- { // 8
- static_assert(test_loop(opt) == 1);
- static_assert(test_loop(unengaged_opt) == 0);
- assert(test_loop(opt) == 1);
- assert(test_loop(unengaged_opt) == 0);
+ // 7: std::ranges::enable_view<optional<T>> == true, and std::format_kind<optional<T>> == true
+ {
+ test_syn();
+ static_assert(test_syn());
}
- { // 9
+ // 8: 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.
+ {
+ test_reset();
static_assert(test_reset());
- assert(test_reset());
}
return 0;
>From 0e1b7bf97162983cad48a5caba2a837b72100b5c Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Fri, 18 Jul 2025 00:37:18 -0400
Subject: [PATCH 4/7] Clang-Format :^)
---
libcxx/test/std/utilities/optional/iterator.pass.cpp | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/libcxx/test/std/utilities/optional/iterator.pass.cpp b/libcxx/test/std/utilities/optional/iterator.pass.cpp
index 3bb831d9dfeb9..7fb7596ec42b0 100644
--- a/libcxx/test/std/utilities/optional/iterator.pass.cpp
+++ b/libcxx/test/std/utilities/optional/iterator.pass.cpp
@@ -47,12 +47,12 @@ constexpr bool test_concepts() {
constexpr bool test_types() {
const std::optional<char> opt{'a'};
std::optional<char> nonconst_opt{'n'};
-
+
assert((std::is_same_v<typename decltype(opt.begin())::value_type, char>));
assert((std::is_same_v<typename decltype(opt.begin())::reference, const char&>));
assert((std::is_same_v<typename decltype(nonconst_opt.begin())::value_type, char>));
assert((std::is_same_v<typename decltype(nonconst_opt.begin())::reference, char&>));
- return true;
+ return true;
}
constexpr bool test_size() {
@@ -112,7 +112,6 @@ constexpr bool test_reset() {
return true;
}
-
int main(int, char**) {
// 1: optional::iterator and optional const_iterator satisfy contiguous_iterator and random_access_iterator
{
>From 9b47577c3922291c019ea683ab7bc40786656c7f Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Fri, 18 Jul 2025 20:58:43 -0400
Subject: [PATCH 5/7] Mostly revert conditional inclusion change (for now) due
to a circular inclusion in __pstl/default.h in C++17 and C++20
The circular include looks like the following:
optional -> __format/range_format.h -> __format/concepts.h -> __format/format_parse_context.h -> string_view -> algorithm -> __algorithm/pstl.h -> __pstl/backend.h -> __pstl/backends/default.h -> optional
---
libcxx/include/optional | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/libcxx/include/optional b/libcxx/include/optional
index caa21000103de..7fb33eee1c949 100644
--- a/libcxx/include/optional
+++ b/libcxx/include/optional
@@ -201,7 +201,6 @@ namespace std {
# include <__config>
# include <__cstddef/ptrdiff_t.h>
# include <__exception/exception.h>
-# include <__format/range_format.h>
# include <__functional/hash.h>
# include <__functional/invoke.h>
# include <__functional/unary_function.h>
@@ -250,6 +249,10 @@ namespace std {
# include <initializer_list>
# include <version>
+# if _LIBCPP_STD_VER >= 26
+# include <__format/range_format.h>
+# endif
+
// standard-mandated includes
// [optional.syn]
>From 9f08a74b605b0fd273c8654d5e3b75710658cab8 Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Fri, 18 Jul 2025 22:58:47 -0400
Subject: [PATCH 6/7] Remove mentions of implementation in Libc++ 21, should go
to Libc++ 22, also remove check_assertion.h header in test due to it breaking
---
libcxx/docs/ReleaseNotes/21.rst | 1 -
libcxx/docs/Status/Cxx2cPapers.csv | 2 +-
libcxx/test/std/utilities/optional/iterator.pass.cpp | 1 -
3 files changed, 1 insertion(+), 3 deletions(-)
diff --git a/libcxx/docs/ReleaseNotes/21.rst b/libcxx/docs/ReleaseNotes/21.rst
index e07f825fd19cc..d31ca0130cb80 100644
--- a/libcxx/docs/ReleaseNotes/21.rst
+++ b/libcxx/docs/ReleaseNotes/21.rst
@@ -53,7 +53,6 @@ Implemented Papers
- P2711R1: Making multi-param constructors of ``views`` ``explicit`` (`Github <https://github.com/llvm/llvm-project/issues/105252>`__)
- P2770R0: Stashing stashing ``iterators`` for proper flattening (`Github <https://github.com/llvm/llvm-project/issues/105250>`__)
- P2655R3: ``common_reference_t`` of ``reference_wrapper`` Should Be a Reference Type (`Github <https://github.com/llvm/llvm-project/issues/105260>`__)
-- 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 3fc2a99b1671d..9385004238f95 100644
--- a/libcxx/docs/Status/Cxx2cPapers.csv
+++ b/libcxx/docs/Status/Cxx2cPapers.csv
@@ -66,7 +66,7 @@
"`P2747R2 <https://wg21.link/P2747R2>`__","``constexpr`` placement new","2024-06 (St. Louis)","|Complete|","20",""
"`P2997R1 <https://wg21.link/P2997R1>`__","Removing the common reference requirement from the indirectly invocable concepts","2024-06 (St. Louis)","|Complete|","19","Implemented as a DR against C++20. (MSVC STL and libstdc++ will do the same.)"
"`P2389R2 <https://wg21.link/P2389R2>`__","``dextents`` Index Type Parameter","2024-06 (St. Louis)","|Complete|","19",""
-"`P3168R2 <https://wg21.link/P3168R2>`__","Give ``std::optional`` Range Support","2024-06 (St. Louis)","|Complete|","21",""
+"`P3168R2 <https://wg21.link/P3168R2>`__","Give ``std::optional`` Range Support","2024-06 (St. Louis)","|Complete|","22",""
"`P3217R0 <https://wg21.link/P3217R0>`__","Adjoints to 'Enabling list-initialization for algorithms': find_last","2024-06 (St. Louis)","","",""
"`P2985R0 <https://wg21.link/P2985R0>`__","A type trait for detecting virtual base classes","2024-06 (St. Louis)","|Complete|","20",""
"`P0843R14 <https://wg21.link/P0843R14>`__","``inplace_vector``","2024-06 (St. Louis)","","",""
diff --git a/libcxx/test/std/utilities/optional/iterator.pass.cpp b/libcxx/test/std/utilities/optional/iterator.pass.cpp
index 7fb7596ec42b0..bc9acfddc14f3 100644
--- a/libcxx/test/std/utilities/optional/iterator.pass.cpp
+++ b/libcxx/test/std/utilities/optional/iterator.pass.cpp
@@ -25,7 +25,6 @@
#include <type_traits>
#include "test_macros.h"
-#include "check_assertion.h"
constexpr bool test_concepts() {
const std::optional<char> opt{'a'};
>From 843f616170a6be0ed92dac8a148e90711c304c51 Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Sat, 19 Jul 2025 12:06:17 -0400
Subject: [PATCH 7/7] Add entry to libc++ 22 release notes
---
libcxx/docs/ReleaseNotes/22.rst | 3 +++
1 file changed, 3 insertions(+)
diff --git a/libcxx/docs/ReleaseNotes/22.rst b/libcxx/docs/ReleaseNotes/22.rst
index cade192260f6d..a9525597eeef2 100644
--- a/libcxx/docs/ReleaseNotes/22.rst
+++ b/libcxx/docs/ReleaseNotes/22.rst
@@ -36,6 +36,9 @@ What's New in Libc++ 22.0.0?
==============================
Implemented Papers
+
+- P3168R2: Give ``std::optional`` Range Support (`Github <https://github.com/llvm/llvm-project/issues/105430>`__)
+
------------------
More information about the libcxx-commits
mailing list