From ac4dcbce204eb6df44dfeb7132997b4ff018d1fe Mon Sep 17 00:00:00 2001 From: William Tran-Viet Date: Mon, 14 Jul 2025 23:13:03 -0400 Subject: [PATCH 01/26] 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 74bfa97fd21c2..66ce56dd53552 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 `__) - P2770R0: Stashing stashing ``iterators`` for proper flattening (`Github `__) - P2655R3: ``common_reference_t`` of ``reference_wrapper`` Should Be a Reference Type (`Github `__) - +- P3168R2 Give ``std::optional`` Range Support (`Github `__) 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 `__","``constexpr`` placement new","2024-06 (St. Louis)","|Complete|","20","" "`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 `__","``dextents`` Index Type Parameter","2024-06 (St. Louis)","|Complete|","19","" -"`P3168R2 `__","Give ``std::optional`` Range Support","2024-06 (St. Louis)","","","" +"`P3168R2 `__","Give ``std::optional`` Range Support","2024-06 (St. Louis)","","21","|Complete|" "`P3217R0 `__","Adjoints to 'Enabling list-initialization for algorithms': find_last","2024-06 (St. Louis)","","","" "`P2985R0 `__","A type trait for detecting virtual base classes","2024-06 (St. Louis)","|Complete|","20","" "`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 friend struct array; + template + friend class optional; }; template 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 optional; + template + constexpr bool ranges::enable_view> = true; + template + constexpr auto format_kind> = range_format::disabled; + template concept is-derived-from-optional = requires(const T& t) { // exposition only [](const optional&){ }(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 # include +# 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 optional; +# if _LIBCPP_STD_VER >= 26 +template +constexpr bool ranges::enable_view> = true; + +template +constexpr range_format format_kind> = range_format::disabled; +# endif + # if _LIBCPP_STD_VER >= 20 template @@ -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>; + using const_pointer = std::add_pointer_t>; +# endif + public: using value_type = _Tp; +# if _LIBCPP_STD_VER >= 26 + using iterator = __wrap_iter; + using const_iterator = __wrap_iter; +# 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 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 # 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 + +// + +// template 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> == true, and std::format_kind> == 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 +#include + +#include "test_macros.h" +#include "check_assertion.h" + +constexpr int test_loop(const std::optional val) { + int size = 0; + for (auto&& v : val) { + std::ignore = v; + size++; + } + return size; +} + +constexpr bool test_reset() { + std::optional 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 opt{'a'}; + constexpr std::optional unengaged_opt; + std::optional nonconst_opt{'n'}; + + // 1 + { + static_assert(std::contiguous_iterator); + static_assert(std::contiguous_iterator); + static_assert(std::random_access_iterator); + static_assert(std::random_access_iterator); + + static_assert(std::contiguous_iterator); + static_assert(std::contiguous_iterator); + static_assert(std::random_access_iterator); + static_assert(std::random_access_iterator); + } + + { // 2 + static_assert(std::same_as); + static_assert(std::same_as); + static_assert(std::same_as); + static_assert(std::same_as); + } + + // 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> == true); + assert(std::format_kind> == 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 3185aa35d5593d7599f3760b76ca2e065622b1e9 Mon Sep 17 00:00:00 2001 From: William Tran-Viet Date: Thu, 17 Jul 2025 23:01:40 -0400 Subject: [PATCH 02/26] 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 `__","``constexpr`` placement new","2024-06 (St. Louis)","|Complete|","20","" "`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 `__","``dextents`` Index Type Parameter","2024-06 (St. Louis)","|Complete|","19","" -"`P3168R2 `__","Give ``std::optional`` Range Support","2024-06 (St. Louis)","","21","|Complete|" +"`P3168R2 `__","Give ``std::optional`` Range Support","2024-06 (St. Louis)","|Complete|","21","" "`P3217R0 `__","Adjoints to 'Enabling list-initialization for algorithms': find_last","2024-06 (St. Louis)","","","" "`P2985R0 `__","A type trait for detecting virtual base classes","2024-06 (St. Louis)","|Complete|","20","" "`P0843R14 `__","``inplace_vector``","2024-06 (St. Louis)","","","" From 60f67b23329a79c8202110df95ab3483e8c0bb8b Mon Sep 17 00:00:00 2001 From: William Tran-Viet Date: Fri, 18 Jul 2025 00:34:27 -0400 Subject: [PATCH 03/26] 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 66ce56dd53552..2dd1115d22d88 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 `__) - P2655R3: ``common_reference_t`` of ``reference_wrapper`` Should Be a Reference Type (`Github `__) - P3168R2 Give ``std::optional`` Range Support (`Github `__) + 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 # include -# 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 @@ // // template 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> == true, and std::format_kind> == 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 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 +#include +#include #include +#include +#include #include "test_macros.h" #include "check_assertion.h" -constexpr int test_loop(const std::optional val) { - int size = 0; - for (auto&& v : val) { - std::ignore = v; - size++; - } - return size; +constexpr bool test_concepts() { + const std::optional opt{'a'}; + std::optional nonconst_opt{'n'}; + + assert(std::contiguous_iterator); + assert(std::contiguous_iterator); + assert(std::random_access_iterator); + assert(std::random_access_iterator); + + assert(std::contiguous_iterator); + assert(std::contiguous_iterator); + assert(std::random_access_iterator); + assert(std::random_access_iterator); + + return true; +} + +constexpr bool test_types() { + const std::optional opt{'a'}; + std::optional nonconst_opt{'n'}; + + assert((std::is_same_v)); + assert((std::is_same_v)); + assert((std::is_same_v)); + assert((std::is_same_v)); + return true; +} + +constexpr bool test_size() { + const std::optional opt{'a'}; + std::optional 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 opt{'a'}; + std::optional unengaged_opt; + + assert(opt.begin() != opt.end()); + assert(unengaged_opt.begin() == unengaged_opt.end()); + return true; +} + +constexpr bool test_noexcept() { + const std::optional opt{'a'}; + std::optional 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 opt{'a'}; + std::optional nonconst_opt{'n'}; + + assert(*(opt.begin()) == 'a'); + assert(*(nonconst_opt.begin()) == 'n'); + return true; +} + +constexpr bool test_syn() { + assert(std::ranges::enable_view> == true); + assert(std::format_kind> == 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 opt{'a'}; - constexpr std::optional unengaged_opt; - std::optional 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); - static_assert(std::contiguous_iterator); - static_assert(std::random_access_iterator); - static_assert(std::random_access_iterator); - - static_assert(std::contiguous_iterator); - static_assert(std::contiguous_iterator); - static_assert(std::random_access_iterator); - static_assert(std::random_access_iterator); - } - - { // 2 - static_assert(std::same_as); - static_assert(std::same_as); - static_assert(std::same_as); - static_assert(std::same_as); + 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> == true); - assert(std::format_kind> == 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> == true, and std::format_kind> == 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 a7740e8b9c467594ea9ec58f35c4a5b764f639e1 Mon Sep 17 00:00:00 2001 From: William Tran-Viet Date: Fri, 18 Jul 2025 00:37:18 -0400 Subject: [PATCH 04/26] 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 opt{'a'}; std::optional nonconst_opt{'n'}; - + assert((std::is_same_v)); assert((std::is_same_v)); assert((std::is_same_v)); assert((std::is_same_v)); - 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 02cdb077102b47764db604efcea49eea549d5ddd Mon Sep 17 00:00:00 2001 From: William Tran-Viet Date: Fri, 18 Jul 2025 20:58:43 -0400 Subject: [PATCH 05/26] 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 # include +# if _LIBCPP_STD_VER >= 26 +# include <__format/range_format.h> +# endif + // standard-mandated includes // [optional.syn] From 913229a5eaf04beab058810249f85879ca0586b0 Mon Sep 17 00:00:00 2001 From: William Tran-Viet Date: Fri, 18 Jul 2025 22:58:47 -0400 Subject: [PATCH 06/26] 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 2dd1115d22d88..74bfa97fd21c2 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 `__) - P2770R0: Stashing stashing ``iterators`` for proper flattening (`Github `__) - P2655R3: ``common_reference_t`` of ``reference_wrapper`` Should Be a Reference Type (`Github `__) -- P3168R2 Give ``std::optional`` Range Support (`Github `__) 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 `__","``constexpr`` placement new","2024-06 (St. Louis)","|Complete|","20","" "`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 `__","``dextents`` Index Type Parameter","2024-06 (St. Louis)","|Complete|","19","" -"`P3168R2 `__","Give ``std::optional`` Range Support","2024-06 (St. Louis)","|Complete|","21","" +"`P3168R2 `__","Give ``std::optional`` Range Support","2024-06 (St. Louis)","|Complete|","22","" "`P3217R0 `__","Adjoints to 'Enabling list-initialization for algorithms': find_last","2024-06 (St. Louis)","","","" "`P2985R0 `__","A type trait for detecting virtual base classes","2024-06 (St. Louis)","|Complete|","20","" "`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 #include "test_macros.h" -#include "check_assertion.h" constexpr bool test_concepts() { const std::optional opt{'a'}; From 559825bca7e833ead8df5472d3995b77e490c9a9 Mon Sep 17 00:00:00 2001 From: William Tran-Viet Date: Sat, 19 Jul 2025 12:06:17 -0400 Subject: [PATCH 07/26] 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 15bf46d44b07f..d5376ad903251 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 `__) + ------------------ - P2321R2: ``zip`` (`Github `__) (The paper is partially implemented. ``zip_transform_view`` is implemented in this release) From 3baa1634246b9710134056d95dd74c0d19c7b7ff Mon Sep 17 00:00:00 2001 From: William Tran-Viet Date: Mon, 21 Jul 2025 19:53:48 -0400 Subject: [PATCH 08/26] implicit bool conversions are evil --- libcxx/include/optional | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libcxx/include/optional b/libcxx/include/optional index 7fb33eee1c949..6b1739dd7e364 100644 --- a/libcxx/include/optional +++ b/libcxx/include/optional @@ -837,8 +837,8 @@ public: 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(); } + _LIBCPP_HIDE_FROM_ABI constexpr iterator end() noexcept { return begin() + (this->has_value() ? 1 : 0); } + _LIBCPP_HIDE_FROM_ABI constexpr const_iterator end() const noexcept { return begin() + (this->has_value() ? 1 : 0); } # endif _LIBCPP_HIDE_FROM_ABI constexpr add_pointer_t operator->() const noexcept { From e3398aea6dabf18780d7e63f4367c9d08da1303a Mon Sep 17 00:00:00 2001 From: William Tran-Viet Date: Mon, 21 Jul 2025 19:53:59 -0400 Subject: [PATCH 09/26] Split test file into three tests --- .../std/utilities/optional/iterator.pass.cpp | 164 ------------------ .../optional/optional.iterator/begin.pass.cpp | 56 ++++++ .../optional/optional.iterator/end.pass.cpp | 68 ++++++++ .../optional.iterator/iterator.pass.cpp | 95 ++++++++++ 4 files changed, 219 insertions(+), 164 deletions(-) delete mode 100644 libcxx/test/std/utilities/optional/iterator.pass.cpp create mode 100644 libcxx/test/std/utilities/optional/optional.iterator/begin.pass.cpp create mode 100644 libcxx/test/std/utilities/optional/optional.iterator/end.pass.cpp create mode 100644 libcxx/test/std/utilities/optional/optional.iterator/iterator.pass.cpp diff --git a/libcxx/test/std/utilities/optional/iterator.pass.cpp b/libcxx/test/std/utilities/optional/iterator.pass.cpp deleted file mode 100644 index bc9acfddc14f3..0000000000000 --- a/libcxx/test/std/utilities/optional/iterator.pass.cpp +++ /dev/null @@ -1,164 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -// REQUIRES: std-at-least-c++26 - -// - -// template class optional::iterator; -// template class optional::const_iterator; -// constexpr iterator optional::begin() noexcept; -// constexpr iterator optional::end() noexcept; -// constexpr const_iterator optional::begin() noexcept; -// constexpr const_iterator optional::end() noexcept; - -#include -#include -#include -#include -#include -#include - -#include "test_macros.h" - -constexpr bool test_concepts() { - const std::optional opt{'a'}; - std::optional nonconst_opt{'n'}; - - assert(std::contiguous_iterator); - assert(std::contiguous_iterator); - assert(std::random_access_iterator); - assert(std::random_access_iterator); - - assert(std::contiguous_iterator); - assert(std::contiguous_iterator); - assert(std::random_access_iterator); - assert(std::random_access_iterator); - - return true; -} - -constexpr bool test_types() { - const std::optional opt{'a'}; - std::optional nonconst_opt{'n'}; - - assert((std::is_same_v)); - assert((std::is_same_v)); - assert((std::is_same_v)); - assert((std::is_same_v)); - return true; -} - -constexpr bool test_size() { - const std::optional opt{'a'}; - std::optional 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 opt{'a'}; - std::optional unengaged_opt; - - assert(opt.begin() != opt.end()); - assert(unengaged_opt.begin() == unengaged_opt.end()); - return true; -} - -constexpr bool test_noexcept() { - const std::optional opt{'a'}; - std::optional 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 opt{'a'}; - std::optional nonconst_opt{'n'}; - - assert(*(opt.begin()) == 'a'); - assert(*(nonconst_opt.begin()) == 'n'); - return true; -} - -constexpr bool test_syn() { - assert(std::ranges::enable_view> == true); - assert(std::format_kind> == std::range_format::disabled); - return true; -} - -constexpr bool test_reset() { - std::optional 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**) { - // 1: optional::iterator and optional const_iterator satisfy contiguous_iterator and random_access_iterator - { - test_concepts(); - static_assert(test_concepts()); - } - - // 2: The value types and reference types for optional::iterator and optional::const_iterator are {_Tp, _Tp&} and {_Tp, const _Tp&} respectively - { - test_types(); - static_assert(test_types()); - } - - // 3: The optional::begin() and optional::end() are marked noexcept. - { - test_noexcept(); - static_assert(test_noexcept()); - } - - // 4: optionals that have a value have begin() != end(), whereas one that doesn't has begin() == end(); - { - test_begin_end(); - static_assert(test_begin_end()); - } - - // 5: The corresponding size for the following optionals is respected: has_value() == 1, !has_value() == 0 - { - test_size(); - static_assert(test_size()); - } - - // 6: Dereferencing an engaged optional's iterator returns the correct value. - { - test_value(); - static_assert(test_value()); - } - - // 7: std::ranges::enable_view> == true, and std::format_kind> == true - { - test_syn(); - static_assert(test_syn()); - } - - // 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()); - } - - return 0; -} diff --git a/libcxx/test/std/utilities/optional/optional.iterator/begin.pass.cpp b/libcxx/test/std/utilities/optional/optional.iterator/begin.pass.cpp new file mode 100644 index 0000000000000..7059510b22620 --- /dev/null +++ b/libcxx/test/std/utilities/optional/optional.iterator/begin.pass.cpp @@ -0,0 +1,56 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// + +// constexpr iterator optional::begin() noexcept; +// constexpr const_iterator optional::begin() noexcept; + +#include +#include +#include +#include + +template +constexpr bool test() { + constexpr std::optional opt{T{}}; + std::optional nonconst_opt{T{}}; + + { // begin() is marked noexcept + assert(noexcept(opt.begin())); + assert(noexcept(nonconst_opt.begin())); + } + + { // Dereferencing an iterator at the beggining as indexing the 0th element, and that those iterators are the same value returned by calling begin() again. + auto iter1 = opt.begin(); + auto iter2 = nonconst_opt.begin(); + assert(*iter1 == iter1[0]); + assert(*iter2 == iter2[0]); + assert(iter1 == opt.begin()); + assert(iter2 == nonconst_opt.begin()); + } + + return true; +} + +constexpr bool tests() { + assert(test()); + assert(test()); + assert(test()); + assert(test()); + return true; +} + +int main() { + assert(tests()); + static_assert(tests()); + + return 0; +} diff --git a/libcxx/test/std/utilities/optional/optional.iterator/end.pass.cpp b/libcxx/test/std/utilities/optional/optional.iterator/end.pass.cpp new file mode 100644 index 0000000000000..65b821f7d2968 --- /dev/null +++ b/libcxx/test/std/utilities/optional/optional.iterator/end.pass.cpp @@ -0,0 +1,68 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// + +// constexpr iterator optional::end() noexcept; +// constexpr const_iterator optional::end() noexcept; + +#include +#include +#include +#include + +template +constexpr bool test() { + std::optional unengaged{std::nullopt}; + constexpr std::optional unengaged2{std::nullopt}; + + { // end() == begin() and end() == end() if the optional is unengaged + auto it = unengaged.end(); + auto it2 = unengaged2.end(); + + assert(it == unengaged.begin()); + assert(unengaged.begin() == it); + assert(it == unengaged.end()); + + assert(it2 == unengaged2.begin()); + assert(unengaged2.begin() == it2); + assert(it2 == unengaged2.end()); + } + + std::optional engaged{T{}}; + constexpr std::optional engaged2{T{}}; + + { // end() != begin() if the optional is engaged + auto it = engaged.end(); + auto it2 = engaged2.end(); + + assert(it != engaged.begin()); + assert(engaged.begin() != it); + + assert(it2 != engaged2.begin()); + assert(engaged2.begin() != it2); + } + + return true; +} + +constexpr bool tests() { + assert(test()); + assert(test()); + assert(test()); + assert(test()); + + return true; +} + +int main() { + assert(tests()); + static_assert(tests()); +} diff --git a/libcxx/test/std/utilities/optional/optional.iterator/iterator.pass.cpp b/libcxx/test/std/utilities/optional/optional.iterator/iterator.pass.cpp new file mode 100644 index 0000000000000..8a70233fc2438 --- /dev/null +++ b/libcxx/test/std/utilities/optional/optional.iterator/iterator.pass.cpp @@ -0,0 +1,95 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++26 + +// + +// template class optional::iterator; +// template class optional::const_iterator; + +#include +#include +#include +#include +#include + +template +constexpr bool test() { + constexpr std::optional opt{__val}; + std::optional nonconst_opt{__val}; + + { // Dereferencing an iterator of an engaged optional will return the same value that the optional holds. + auto it = opt.begin(); + auto it2 = nonconst_opt.begin(); + assert(*it == *opt); + assert(*it2 == *nonconst_opt); + } + + { // optional::iterator and optional::const_iterator satisfy the Cpp17RandomAccessIterator and contiguous iterator. + auto it = opt.begin(); + auto it2 = nonconst_opt.begin(); + assert(std::contiguous_iterator); + assert(std::contiguous_iterator); + + assert(std::random_access_iterator); + assert(std::random_access_iterator); + } + + { // const_iterator::value_type == std::remove_cv_t, const_iterator::reference == const T&, iterator::value_type = std::remove_cv_t, iterator::reference == T& + auto it = opt.begin(); + auto it2 = nonconst_opt.begin(); + assert((std::is_same_v>)); + assert((std::is_same_v)); + assert((std::is_same_v>)); + assert((std::is_same_v)); + } + + { // std::ranges for an engaged optional == 1, unengaged optional == 0 + constexpr std::optional unengaged{std::nullopt}; + std::optional unengaged2{std::nullopt}; + assert(std::ranges::size(opt) == 1); + assert(std::ranges::size(nonconst_opt) == 1); + + assert(std::ranges::size(unengaged) == 0); + assert(std::ranges::size(unengaged2) == 0); + } + + { // std::ranges::enable_view> == true, and std::format_kind> == true + assert(std::ranges::enable_view> == true); + assert(std::format_kind> == std::range_format::disabled); + } + + // 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. + { + std::optional val{__val}; + assert(val.begin() != val.end()); + val.reset(); + assert(val.begin() == val.end()); + val.emplace(__val); + assert(val.begin() != val.end()); + assert(*(val.begin()) == __val); + } + + return true; +} + +constexpr bool tests() { + assert((test())); + assert((test())); + assert((test())); + assert((test())); + assert((test())); + + return true; +} + +int main() { + assert(tests()); + static_assert(tests()); +} From 3b015e7aa7f4593ef7cb1d18394f28c65ee401b0 Mon Sep 17 00:00:00 2001 From: William Tran-Viet Date: Mon, 21 Jul 2025 22:29:43 -0400 Subject: [PATCH 10/26] Remove usage of remove_cvref_t for pointer and const pointer types since they weren't necessary and were messing with _Tp of const types --- libcxx/include/optional | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libcxx/include/optional b/libcxx/include/optional index 6b1739dd7e364..7149a876a549a 100644 --- a/libcxx/include/optional +++ b/libcxx/include/optional @@ -615,8 +615,8 @@ class _LIBCPP_DECLSPEC_EMPTY_BASES optional using __base _LIBCPP_NODEBUG = __optional_move_assign_base<_Tp>; # if _LIBCPP_STD_VER >= 26 - using pointer = std::add_pointer_t>; - using const_pointer = std::add_pointer_t>; + using pointer = std::add_pointer_t<_Tp>; + using const_pointer = std::add_pointer_t; # endif public: From c8318f11224580f6f72eb141bd9098c207f22fda Mon Sep 17 00:00:00 2001 From: William Tran-Viet Date: Mon, 21 Jul 2025 22:41:45 -0400 Subject: [PATCH 11/26] Fix a comment, forgot a noexcept test --- .../std/utilities/optional/optional.iterator/begin.pass.cpp | 2 +- .../std/utilities/optional/optional.iterator/end.pass.cpp | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) 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 7059510b22620..058785080640e 100644 --- a/libcxx/test/std/utilities/optional/optional.iterator/begin.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.iterator/begin.pass.cpp @@ -28,7 +28,7 @@ constexpr bool test() { assert(noexcept(nonconst_opt.begin())); } - { // Dereferencing an iterator at the beggining as indexing the 0th element, and that those iterators are the same value returned by calling begin() again. + { // Dereferencing an iterator at the beginning == indexing the 0th element, and that calling begin() again return the same iterator. auto iter1 = opt.begin(); auto iter2 = nonconst_opt.begin(); assert(*iter1 == iter1[0]); 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 65b821f7d2968..67ad1313e544c 100644 --- a/libcxx/test/std/utilities/optional/optional.iterator/end.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.iterator/end.pass.cpp @@ -23,6 +23,11 @@ constexpr bool test() { std::optional unengaged{std::nullopt}; constexpr std::optional unengaged2{std::nullopt}; + { // end() is marked noexcept + assert(noexcept(opt.end())); + assert(noexcept(nonconst_opt.end())); + } + { // end() == begin() and end() == end() if the optional is unengaged auto it = unengaged.end(); auto it2 = unengaged2.end(); From ebdc19d4811e63582b76e01c17109f3f892559da Mon Sep 17 00:00:00 2001 From: William Tran-Viet Date: Mon, 21 Jul 2025 23:04:40 -0400 Subject: [PATCH 12/26] Copy mistake --- .../std/utilities/optional/optional.iterator/end.pass.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 67ad1313e544c..97d9635b1332b 100644 --- a/libcxx/test/std/utilities/optional/optional.iterator/end.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.iterator/end.pass.cpp @@ -24,8 +24,8 @@ constexpr bool test() { constexpr std::optional unengaged2{std::nullopt}; { // end() is marked noexcept - assert(noexcept(opt.end())); - assert(noexcept(nonconst_opt.end())); + assert(noexcept(unengaged.end())); + assert(noexcept(unengaged2.end())); } { // end() == begin() and end() == end() if the optional is unengaged From 60bf66bfc6527f737418675f6f05ba5adc79a19c Mon Sep 17 00:00:00 2001 From: William Tran-Viet Date: Mon, 28 Jul 2025 17:51:05 -0400 Subject: [PATCH 13/26] Add hardening with bounded_iter --- ...-hardening-mode-fast-with-abi-breaks.cmake | 1 + libcxx/include/optional | 24 ++++++++++++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/libcxx/cmake/caches/Generic-hardening-mode-fast-with-abi-breaks.cmake b/libcxx/cmake/caches/Generic-hardening-mode-fast-with-abi-breaks.cmake index 699d3f8866861..d4ce32ce5b17f 100644 --- a/libcxx/cmake/caches/Generic-hardening-mode-fast-with-abi-breaks.cmake +++ b/libcxx/cmake/caches/Generic-hardening-mode-fast-with-abi-breaks.cmake @@ -5,5 +5,6 @@ set(_defines _LIBCPP_ABI_BOUNDED_ITERATORS_IN_VECTOR _LIBCPP_ABI_BOUNDED_UNIQUE_PTR _LIBCPP_ABI_BOUNDED_ITERATORS_IN_STD_ARRAY + _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL ) set(LIBCXX_ABI_DEFINES "${_defines}" CACHE STRING "") diff --git a/libcxx/include/optional b/libcxx/include/optional index 7149a876a549a..310abe9a550eb 100644 --- a/libcxx/include/optional +++ b/libcxx/include/optional @@ -205,6 +205,7 @@ namespace std { # include <__functional/invoke.h> # include <__functional/unary_function.h> # include <__fwd/functional.h> +# include <__iterator/bounded_iter.h> # include <__iterator/wrap_iter.h> # include <__memory/addressof.h> # include <__memory/construct_at.h> @@ -622,8 +623,13 @@ class _LIBCPP_DECLSPEC_EMPTY_BASES optional public: using value_type = _Tp; # if _LIBCPP_STD_VER >= 26 +# ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL + using iterator = __bounded_iter<__wrap_iter>; + using const_iterator = __bounded_iter<__wrap_iter>; +# else using iterator = __wrap_iter; using const_iterator = __wrap_iter; +# endif # endif using __trivially_relocatable _LIBCPP_NODEBUG = @@ -831,10 +837,26 @@ 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 iterator begin() noexcept { +# ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL + return std::__make_bounded_iter( + std::__wrap_iter(std::addressof(this->__get())), + std::__wrap_iter(std::addressof(this->__get())), + std::__wrap_iter(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(std::addressof(this->__get())), + std::__wrap_iter(std::addressof(this->__get())), + std::__wrap_iter(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); } From 368346926c5ccc87fa0c08dbf7aea4811df538b2 Mon Sep 17 00:00:00 2001 From: William Tran-Viet Date: Tue, 29 Jul 2025 17:47:20 -0400 Subject: [PATCH 14/26] Fix misplaced parens --- libcxx/include/optional | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libcxx/include/optional b/libcxx/include/optional index 310abe9a550eb..936e0f38c5c09 100644 --- a/libcxx/include/optional +++ b/libcxx/include/optional @@ -842,7 +842,7 @@ public: return std::__make_bounded_iter( std::__wrap_iter(std::addressof(this->__get())), std::__wrap_iter(std::addressof(this->__get())), - std::__wrap_iter(std::addressof(this->__get()) + (this->has_value()) ? 1 : 0)); + std::__wrap_iter(std::addressof(this->__get()) + (this->has_value() ? 1 : 0))); # else return iterator(std::addressof(this->__get())); # endif @@ -853,7 +853,7 @@ public: return std::__make_bounded_iter( std::__wrap_iter(std::addressof(this->__get())), std::__wrap_iter(std::addressof(this->__get())), - std::__wrap_iter(std::addressof(this->__get()) + (this->has_value()) ? 1 : 0)); + std::__wrap_iter(std::addressof(this->__get()) + (this->has_value() ? 1 : 0))); # else return const_iterator(std::addressof(this->__get())); # endif From e9ec7332fdf8b68b3adb1ca7ce7f1a6c6186c16d Mon Sep 17 00:00:00 2001 From: William Tran-Viet Date: Tue, 29 Jul 2025 18:48:20 -0400 Subject: [PATCH 15/26] Remove some transitive includes that were removed with #150583 --- .../test/libcxx/transitive_includes/cxx26.csv | 47 ------------------- 1 file changed, 47 deletions(-) diff --git a/libcxx/test/libcxx/transitive_includes/cxx26.csv b/libcxx/test/libcxx/transitive_includes/cxx26.csv index 7d383d5c8e819..5f906338f4b7c 100644 --- a/libcxx/test/libcxx/transitive_includes/cxx26.csv +++ b/libcxx/test/libcxx/transitive_includes/cxx26.csv @@ -2,7 +2,6 @@ algorithm cctype algorithm climits algorithm compare algorithm cstdint -algorithm cstdio algorithm cstring algorithm ctime algorithm cwchar @@ -12,8 +11,6 @@ algorithm iosfwd algorithm limits algorithm optional algorithm ratio -algorithm stdexcept -algorithm string_view algorithm tuple algorithm version any cstdint @@ -333,32 +330,26 @@ 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 @@ -426,16 +417,13 @@ 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 @@ -635,16 +623,13 @@ 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 @@ -688,36 +673,22 @@ 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 @@ -835,7 +806,6 @@ ranges limits ranges optional ranges span ranges stdexcept -ranges string_view ranges tuple ranges variant ranges version @@ -881,16 +851,12 @@ 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 @@ -1125,34 +1091,21 @@ 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 From 97f3a7bd109a814fb424418bb6527e5be4ee87a1 Mon Sep 17 00:00:00 2001 From: William Tran-Viet Date: Wed, 30 Jul 2025 19:34:36 -0400 Subject: [PATCH 16/26] Remove include guard since it's no longer necessary --- libcxx/include/optional | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/libcxx/include/optional b/libcxx/include/optional index 936e0f38c5c09..f354005d8c772 100644 --- a/libcxx/include/optional +++ b/libcxx/include/optional @@ -201,6 +201,7 @@ 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,10 +251,6 @@ namespace std { # include # include -# if _LIBCPP_STD_VER >= 26 -# include <__format/range_format.h> -# endif - // standard-mandated includes // [optional.syn] From 7cab02131ce0a218244dc336580e71424317ea87 Mon Sep 17 00:00:00 2001 From: William Tran-Viet Date: Thu, 31 Jul 2025 17:57:30 -0400 Subject: [PATCH 17/26] Address comments --- .../optional/optional.iterator/begin.pass.cpp | 17 ++++++++++---- .../optional/optional.iterator/end.pass.cpp | 12 ++++++---- .../optional.iterator/iterator.pass.cpp | 23 +++++++++++-------- 3 files changed, 33 insertions(+), 19 deletions(-) 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 058785080640e..f5b5fcfb5893f 100644 --- a/libcxx/test/std/utilities/optional/optional.iterator/begin.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.iterator/begin.pass.cpp @@ -17,15 +17,16 @@ #include #include #include +#include template constexpr bool test() { - constexpr std::optional opt{T{}}; + const std::optional opt{T{}}; std::optional nonconst_opt{T{}}; { // begin() is marked noexcept - assert(noexcept(opt.begin())); - assert(noexcept(nonconst_opt.begin())); + static_assert(noexcept(opt.begin())); + static_assert(noexcept(nonconst_opt.begin())); } { // Dereferencing an iterator at the beginning == indexing the 0th element, and that calling begin() again return the same iterator. @@ -37,6 +38,14 @@ constexpr bool test() { assert(iter2 == nonconst_opt.begin()); } + { // Calling begin() multiple times on a disengaged optional returns the same iterator. + std::optional disengaged{std::nullopt}; + auto iter1 = disengaged.begin(); + auto iter2 = std::as_const(disengaged).begin(); + assert(iter1 == disengaged.begin()); + assert(iter2 == std::as_const(disengaged).begin()); + } + return true; } @@ -48,7 +57,7 @@ constexpr bool tests() { return true; } -int main() { +int main(int, char**) { assert(tests()); static_assert(tests()); 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 97d9635b1332b..5e2a2c81bf4f5 100644 --- a/libcxx/test/std/utilities/optional/optional.iterator/end.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.iterator/end.pass.cpp @@ -21,11 +21,11 @@ template constexpr bool test() { std::optional unengaged{std::nullopt}; - constexpr std::optional unengaged2{std::nullopt}; + const std::optional unengaged2{std::nullopt}; { // end() is marked noexcept - assert(noexcept(unengaged.end())); - assert(noexcept(unengaged2.end())); + static_assert(noexcept(unengaged.end())); + static_assert(noexcept(unengaged2.end())); } { // end() == begin() and end() == end() if the optional is unengaged @@ -42,7 +42,7 @@ constexpr bool test() { } std::optional engaged{T{}}; - constexpr std::optional engaged2{T{}}; + const std::optional engaged2{T{}}; { // end() != begin() if the optional is engaged auto it = engaged.end(); @@ -67,7 +67,9 @@ constexpr bool tests() { return true; } -int main() { +int main(int, char**) { assert(tests()); static_assert(tests()); + + return 0; } 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 8a70233fc2438..3ebeb811837b6 100644 --- a/libcxx/test/std/utilities/optional/optional.iterator/iterator.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.iterator/iterator.pass.cpp @@ -21,7 +21,7 @@ template constexpr bool test() { - constexpr std::optional opt{__val}; + const std::optional opt{__val}; std::optional nonconst_opt{__val}; { // Dereferencing an iterator of an engaged optional will return the same value that the optional holds. @@ -50,22 +50,23 @@ constexpr bool test() { assert((std::is_same_v)); } - { // std::ranges for an engaged optional == 1, unengaged optional == 0 - constexpr std::optional unengaged{std::nullopt}; - std::optional unengaged2{std::nullopt}; + { // std::ranges::size for an engaged optional == 1, disengaged optional == 0 + const std::optional disengaged{std::nullopt}; + std::optional disengaged2{std::nullopt}; assert(std::ranges::size(opt) == 1); assert(std::ranges::size(nonconst_opt) == 1); - assert(std::ranges::size(unengaged) == 0); - assert(std::ranges::size(unengaged2) == 0); + assert(std::ranges::size(disengaged) == 0); + assert(std::ranges::size(disengaged2) == 0); } { // std::ranges::enable_view> == true, and std::format_kind> == true - assert(std::ranges::enable_view> == true); - assert(std::format_kind> == std::range_format::disabled); + static_assert(std::ranges::enable_view> == true); + static_assert(std::format_kind> == std::range_format::disabled); } - // 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. + // An optional with value that is reset will have a begin() == end(), then when it is reassigned a value, + // begin() != end(), and *begin() will contain the new value. { std::optional val{__val}; assert(val.begin() != val.end()); @@ -89,7 +90,9 @@ constexpr bool tests() { return true; } -int main() { +int main(int, char**) { assert(tests()); static_assert(tests()); + + return 0; } From c0ff08ab4daea0a5fac3334baf08a9ff3bda3c3b Mon Sep 17 00:00:00 2001 From: William Tran-Viet Date: Thu, 31 Jul 2025 18:00:37 -0400 Subject: [PATCH 18/26] Forgot to rename a variable --- .../optional/optional.iterator/end.pass.cpp | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) 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 5e2a2c81bf4f5..52eef853fcf7e 100644 --- a/libcxx/test/std/utilities/optional/optional.iterator/end.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.iterator/end.pass.cpp @@ -20,25 +20,25 @@ template constexpr bool test() { - std::optional unengaged{std::nullopt}; - const std::optional unengaged2{std::nullopt}; + std::optional disengaged{std::nullopt}; + const std::optional disengaged2{std::nullopt}; { // end() is marked noexcept - static_assert(noexcept(unengaged.end())); - static_assert(noexcept(unengaged2.end())); + static_assert(noexcept(disengaged.end())); + static_assert(noexcept(disengaged2.end())); } - { // end() == begin() and end() == end() if the optional is unengaged - auto it = unengaged.end(); - auto it2 = unengaged2.end(); + { // end() == begin() and end() == end() if the optional is disengaged + auto it = disengaged.end(); + auto it2 = disengaged2.end(); - assert(it == unengaged.begin()); - assert(unengaged.begin() == it); - assert(it == unengaged.end()); + assert(it == disengaged.begin()); + assert(disengaged.begin() == it); + assert(it == disengaged.end()); - assert(it2 == unengaged2.begin()); - assert(unengaged2.begin() == it2); - assert(it2 == unengaged2.end()); + assert(it2 == disengaged2.begin()); + assert(disengaged2.begin() == it2); + assert(it2 == disengaged2.end()); } std::optional engaged{T{}}; From 718af43783b29c97bbef9300f5e7b3d728ed55df Mon Sep 17 00:00:00 2001 From: William Tran-Viet Date: Mon, 4 Aug 2025 20:58:15 -0400 Subject: [PATCH 19/26] Use std::as_const pattern for all tests --- .../optional/optional.iterator/begin.pass.cpp | 11 ++++----- .../optional/optional.iterator/end.pass.cpp | 23 +++++++++---------- .../optional.iterator/iterator.pass.cpp | 17 +++++++------- 3 files changed, 24 insertions(+), 27 deletions(-) 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 f5b5fcfb5893f..df95a8df3793f 100644 --- a/libcxx/test/std/utilities/optional/optional.iterator/begin.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.iterator/begin.pass.cpp @@ -11,7 +11,7 @@ // // constexpr iterator optional::begin() noexcept; -// constexpr const_iterator optional::begin() noexcept; +// constexpr const_iterator optional::begin() const noexcept; #include #include @@ -21,21 +21,20 @@ template constexpr bool test() { - const std::optional opt{T{}}; - std::optional nonconst_opt{T{}}; + std::optional opt{T{}}; { // begin() is marked noexcept static_assert(noexcept(opt.begin())); - static_assert(noexcept(nonconst_opt.begin())); + static_assert(noexcept(std::as_const(opt).begin())); } { // Dereferencing an iterator at the beginning == indexing the 0th element, and that calling begin() again return the same iterator. auto iter1 = opt.begin(); - auto iter2 = nonconst_opt.begin(); + auto iter2 = std::as_const(opt).begin(); assert(*iter1 == iter1[0]); assert(*iter2 == iter2[0]); assert(iter1 == opt.begin()); - assert(iter2 == nonconst_opt.begin()); + assert(iter2 == std::as_const(opt).begin()); } { // Calling begin() multiple times on a disengaged optional returns the same iterator. 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 52eef853fcf7e..966c3e7441880 100644 --- a/libcxx/test/std/utilities/optional/optional.iterator/end.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.iterator/end.pass.cpp @@ -11,48 +11,47 @@ // // constexpr iterator optional::end() noexcept; -// constexpr const_iterator optional::end() noexcept; +// constexpr const_iterator optional::end() const noexcept; #include #include -#include #include +#include +#include template constexpr bool test() { std::optional disengaged{std::nullopt}; - const std::optional disengaged2{std::nullopt}; { // end() is marked noexcept static_assert(noexcept(disengaged.end())); - static_assert(noexcept(disengaged2.end())); + static_assert(noexcept(std::as_const(disengaged).end())); } { // end() == begin() and end() == end() if the optional is disengaged auto it = disengaged.end(); - auto it2 = disengaged2.end(); + auto it2 = std::as_const(disengaged).end(); assert(it == disengaged.begin()); assert(disengaged.begin() == it); assert(it == disengaged.end()); - assert(it2 == disengaged2.begin()); - assert(disengaged2.begin() == it2); - assert(it2 == disengaged2.end()); + assert(it2 == std::as_const(disengaged).begin()); + assert(std::as_const(disengaged).begin() == it2); + assert(it2 == std::as_const(disengaged).end()); } std::optional engaged{T{}}; - const std::optional engaged2{T{}}; { // end() != begin() if the optional is engaged auto it = engaged.end(); - auto it2 = engaged2.end(); + auto it2 = std::as_const(engaged).end(); assert(it != engaged.begin()); assert(engaged.begin() != it); - assert(it2 != engaged2.begin()); - assert(engaged2.begin() != it2); + assert(it2 != std::as_const(engaged).begin()); + assert(std::as_const(engaged).begin() != it2); } 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 3ebeb811837b6..9871e43b774ce 100644 --- a/libcxx/test/std/utilities/optional/optional.iterator/iterator.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.iterator/iterator.pass.cpp @@ -21,19 +21,18 @@ template constexpr bool test() { - const std::optional opt{__val}; - std::optional nonconst_opt{__val}; + std::optional opt{__val}; { // Dereferencing an iterator of an engaged optional will return the same value that the optional holds. auto it = opt.begin(); - auto it2 = nonconst_opt.begin(); + auto it2 = std::as_const(opt).begin(); assert(*it == *opt); - assert(*it2 == *nonconst_opt); + assert(*it2 == *std::as_const(opt)); } { // optional::iterator and optional::const_iterator satisfy the Cpp17RandomAccessIterator and contiguous iterator. auto it = opt.begin(); - auto it2 = nonconst_opt.begin(); + auto it2 = std::as_const(opt).begin(); assert(std::contiguous_iterator); assert(std::contiguous_iterator); @@ -43,18 +42,18 @@ constexpr bool test() { { // const_iterator::value_type == std::remove_cv_t, const_iterator::reference == const T&, iterator::value_type = std::remove_cv_t, iterator::reference == T& auto it = opt.begin(); - auto it2 = nonconst_opt.begin(); + auto it2 = std::as_const(opt).begin(); assert((std::is_same_v>)); - assert((std::is_same_v)); + assert((std::is_same_v)); assert((std::is_same_v>)); - assert((std::is_same_v)); + assert((std::is_same_v)); } { // std::ranges::size for an engaged optional == 1, disengaged optional == 0 const std::optional disengaged{std::nullopt}; std::optional disengaged2{std::nullopt}; assert(std::ranges::size(opt) == 1); - assert(std::ranges::size(nonconst_opt) == 1); + assert(std::ranges::size(std::as_const(opt)) == 1); assert(std::ranges::size(disengaged) == 0); assert(std::ranges::size(disengaged2) == 0); From 5c60392661ca1ec436a17c513fa0f9c795c34f6c Mon Sep 17 00:00:00 2001 From: William Tran-Viet Date: Mon, 4 Aug 2025 22:04:45 -0400 Subject: [PATCH 20/26] Rename pointer type and unguard it --- libcxx/include/optional | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/libcxx/include/optional b/libcxx/include/optional index f354005d8c772..d463c7370e531 100644 --- a/libcxx/include/optional +++ b/libcxx/include/optional @@ -612,20 +612,18 @@ 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<_Tp>; - using const_pointer = std::add_pointer_t; -# endif + using __pointer _LIBCPP_NODEBUG = std::add_pointer_t<_Tp>; + using __const_pointer _LIBCPP_NODEBUG = std::add_pointer_t; public: using value_type = _Tp; # if _LIBCPP_STD_VER >= 26 # ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL - using iterator = __bounded_iter<__wrap_iter>; - using const_iterator = __bounded_iter<__wrap_iter>; + using iterator = __bounded_iter<__wrap_iter<__pointer>>; + using const_iterator = __bounded_iter<__wrap_iter<__const_pointer>>; # else - using iterator = __wrap_iter; - using const_iterator = __wrap_iter; + using iterator = __wrap_iter<__pointer>; + using const_iterator = __wrap_iter<__const_pointer>; # endif # endif @@ -837,9 +835,9 @@ public: _LIBCPP_HIDE_FROM_ABI constexpr iterator begin() noexcept { # ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL return std::__make_bounded_iter( - std::__wrap_iter(std::addressof(this->__get())), - std::__wrap_iter(std::addressof(this->__get())), - std::__wrap_iter(std::addressof(this->__get()) + (this->has_value() ? 1 : 0))); + 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 @@ -848,9 +846,9 @@ public: _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(std::addressof(this->__get())), - std::__wrap_iter(std::addressof(this->__get())), - std::__wrap_iter(std::addressof(this->__get()) + (this->has_value() ? 1 : 0))); + 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 From d81e0176261ab9674d07513035ab11a8a44b5d3d Mon Sep 17 00:00:00 2001 From: William Tran-Viet Date: Tue, 5 Aug 2025 19:50:46 -0400 Subject: [PATCH 21/26] Disallow T(&)[] and T&(Args...) from having iterator types as pre-caution --- libcxx/include/optional | 42 ++++++++++++++----- .../iterator.compile.pass.cpp | 30 +++++++++++++ 2 files changed, 61 insertions(+), 11 deletions(-) create mode 100644 libcxx/test/libcxx/utilities/optional/optional.iterator/iterator.compile.pass.cpp diff --git a/libcxx/include/optional b/libcxx/include/optional index d463c7370e531..47ff33414ddbe 100644 --- a/libcxx/include/optional +++ b/libcxx/include/optional @@ -225,6 +225,7 @@ namespace std { # include <__type_traits/is_convertible.h> # include <__type_traits/is_core_convertible.h> # include <__type_traits/is_destructible.h> +# include <__type_traits/is_function.h> # include <__type_traits/is_nothrow_assignable.h> # include <__type_traits/is_nothrow_constructible.h> # include <__type_traits/is_object.h> @@ -237,6 +238,7 @@ namespace std { # include <__type_traits/is_trivially_constructible.h> # include <__type_traits/is_trivially_destructible.h> # include <__type_traits/is_trivially_relocatable.h> +# include <__type_traits/is_unbounded_array.h> # include <__type_traits/negation.h> # include <__type_traits/remove_const.h> # include <__type_traits/remove_cv.h> @@ -605,28 +607,46 @@ struct __is_std_optional : false_type {}; template struct __is_std_optional> : true_type {}; -template -class _LIBCPP_DECLSPEC_EMPTY_BASES optional - : private __optional_move_assign_base<_Tp>, - private __optional_sfinae_ctor_base_t<_Tp>, - private __optional_sfinae_assign_base_t<_Tp> { - using __base _LIBCPP_NODEBUG = __optional_move_assign_base<_Tp>; +template +struct __optional_iterator_aliases {}; +// disallow T (&)() and T (&)[] +template +struct __optional_iterator_aliases< + _Tp, + __enable_if_t::value && (is_function<__libcpp_remove_reference_t<_Tp>>::value || + is_unbounded_array<__libcpp_remove_reference_t<_Tp>>::value))> > { +private: using __pointer _LIBCPP_NODEBUG = std::add_pointer_t<_Tp>; using __const_pointer _LIBCPP_NODEBUG = std::add_pointer_t; public: - using value_type = _Tp; # if _LIBCPP_STD_VER >= 26 # ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL - using iterator = __bounded_iter<__wrap_iter<__pointer>>; - using const_iterator = __bounded_iter<__wrap_iter<__const_pointer>>; + using __iterator _LIBCPP_NODEBUG = __bounded_iter<__wrap_iter<__pointer>>; + using __const_iterator _LIBCPP_NODEBUG = __bounded_iter<__wrap_iter<__const_pointer>>; # else - using iterator = __wrap_iter<__pointer>; - using const_iterator = __wrap_iter<__const_pointer>; + using __iterator _LIBCPP_NODEBUG = __wrap_iter<__pointer>; + using __const_iterator _LIBCPP_NODEBUG = __wrap_iter<__const_pointer>; # endif # endif +}; + +template +class _LIBCPP_DECLSPEC_EMPTY_BASES optional + : private __optional_move_assign_base<_Tp>, + private __optional_sfinae_ctor_base_t<_Tp>, + private __optional_sfinae_assign_base_t<_Tp>, + public __optional_iterator_aliases<_Tp> { + using __base _LIBCPP_NODEBUG = __optional_move_assign_base<_Tp>; +public: + using value_type = _Tp; + +# if _LIBCPP_STD_VER >= 26 + using iterator = __optional_iterator_aliases<_Tp>::__iterator; + using const_iterator = __optional_iterator_aliases<_Tp>::__const_iterator; +# endif 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>; 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 new file mode 100644 index 0000000000000..3cdd7553e2e5d --- /dev/null +++ b/libcxx/test/libcxx/utilities/optional/optional.iterator/iterator.compile.pass.cpp @@ -0,0 +1,30 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++26 + +// + +// template class optional::iterator; +// template class optional::const_iterator; + +#include + +template +concept has_iterator_aliases = requires { + typename T::iterator; + typename T::const_iterator; +}; + +static_assert(has_iterator_aliases>); +static_assert(has_iterator_aliases>); + +// TODO: Uncomment these once P2988R12 is implemented, as they would be testing optional + +// static_assert(!has_iterator_aliases>); +// static_assert(!has_iterator_aliases>); From 10a2271b2659f6a845df564fcfaf5d960b61c9b4 Mon Sep 17 00:00:00 2001 From: William Tran-Viet Date: Tue, 5 Aug 2025 20:05:58 -0400 Subject: [PATCH 22/26] Forgot an #include --- .../std/utilities/optional/optional.iterator/iterator.pass.cpp | 1 + 1 file changed, 1 insertion(+) 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 9871e43b774ce..1203290a0290a 100644 --- a/libcxx/test/std/utilities/optional/optional.iterator/iterator.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.iterator/iterator.pass.cpp @@ -18,6 +18,7 @@ #include #include #include +#include template constexpr bool test() { From 65b53afe3dd7f1f82f976601a0ce11321d8bdf2c Mon Sep 17 00:00:00 2001 From: William Tran-Viet Date: Tue, 5 Aug 2025 22:26:00 -0400 Subject: [PATCH 23/26] Retrigger Build From 1d9ac9ae5615ea6a708dfdf9e1fd12b7271b53d3 Mon Sep 17 00:00:00 2001 From: William Tran-Viet Date: Wed, 6 Aug 2025 00:30:48 -0400 Subject: [PATCH 24/26] is_unbounded_array is >= C++20 --- libcxx/include/optional | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libcxx/include/optional b/libcxx/include/optional index 47ff33414ddbe..50bc16c1de0cd 100644 --- a/libcxx/include/optional +++ b/libcxx/include/optional @@ -610,6 +610,7 @@ struct __is_std_optional> : true_type {}; template struct __optional_iterator_aliases {}; +# if _LIBCPP_STD_VER >= 26 // disallow T (&)() and T (&)[] template struct __optional_iterator_aliases< @@ -621,7 +622,6 @@ private: using __const_pointer _LIBCPP_NODEBUG = std::add_pointer_t; public: -# if _LIBCPP_STD_VER >= 26 # ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL using __iterator _LIBCPP_NODEBUG = __bounded_iter<__wrap_iter<__pointer>>; using __const_iterator _LIBCPP_NODEBUG = __bounded_iter<__wrap_iter<__const_pointer>>; @@ -629,9 +629,10 @@ public: using __iterator _LIBCPP_NODEBUG = __wrap_iter<__pointer>; using __const_iterator _LIBCPP_NODEBUG = __wrap_iter<__const_pointer>; # endif -# endif }; +# endif + template class _LIBCPP_DECLSPEC_EMPTY_BASES optional : private __optional_move_assign_base<_Tp>, From d04201c70ae3a217496c82bb4e5f1dab00f862eb Mon Sep 17 00:00:00 2001 From: William Tran-Viet Date: Wed, 6 Aug 2025 03:34:08 -0400 Subject: [PATCH 25/26] Bring in __pointer types too, there is probably a better way to do this--this is to avoid cluttering up error messages with __iter_aliases substitution failures when an invalid type for optional is passed --- libcxx/include/optional | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/libcxx/include/optional b/libcxx/include/optional index 50bc16c1de0cd..9d8e8c403f772 100644 --- a/libcxx/include/optional +++ b/libcxx/include/optional @@ -617,17 +617,15 @@ struct __optional_iterator_aliases< _Tp, __enable_if_t::value && (is_function<__libcpp_remove_reference_t<_Tp>>::value || is_unbounded_array<__libcpp_remove_reference_t<_Tp>>::value))> > { -private: +public: using __pointer _LIBCPP_NODEBUG = std::add_pointer_t<_Tp>; using __const_pointer _LIBCPP_NODEBUG = std::add_pointer_t; - -public: # ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL - using __iterator _LIBCPP_NODEBUG = __bounded_iter<__wrap_iter<__pointer>>; - using __const_iterator _LIBCPP_NODEBUG = __bounded_iter<__wrap_iter<__const_pointer>>; + using iterator = __bounded_iter<__wrap_iter<__pointer>>; + using const_iterator = __bounded_iter<__wrap_iter<__const_pointer>>; # else - using __iterator _LIBCPP_NODEBUG = __wrap_iter<__pointer>; - using __const_iterator _LIBCPP_NODEBUG = __wrap_iter<__const_pointer>; + using iterator = __wrap_iter<__pointer>; + using const_iterator = __wrap_iter<__const_pointer>; # endif }; @@ -639,14 +637,19 @@ class _LIBCPP_DECLSPEC_EMPTY_BASES optional private __optional_sfinae_ctor_base_t<_Tp>, private __optional_sfinae_assign_base_t<_Tp>, public __optional_iterator_aliases<_Tp> { - using __base _LIBCPP_NODEBUG = __optional_move_assign_base<_Tp>; + using __base _LIBCPP_NODEBUG = __optional_move_assign_base<_Tp>; + using __iter_aliases _LIBCPP_NODEBUG = __optional_iterator_aliases<_Tp>; +# if _LIBCPP_STD_VER >= 26 + using typename __iter_aliases::__const_pointer; + using typename __iter_aliases::__pointer; +# endif public: using value_type = _Tp; # if _LIBCPP_STD_VER >= 26 - using iterator = __optional_iterator_aliases<_Tp>::__iterator; - using const_iterator = __optional_iterator_aliases<_Tp>::__const_iterator; + using typename __iter_aliases::const_iterator; + using typename __iter_aliases::iterator; # endif using __trivially_relocatable _LIBCPP_NODEBUG = conditional_t<__libcpp_is_trivially_relocatable<_Tp>::value, optional, void>; From e9d0fa7497f885203faebd988a5da6eb53b4a272 Mon Sep 17 00:00:00 2001 From: William Tran-Viet Date: Wed, 6 Aug 2025 17:28:38 -0400 Subject: [PATCH 26/26] Remove unnecessary public: --- libcxx/include/optional | 1 - 1 file changed, 1 deletion(-) diff --git a/libcxx/include/optional b/libcxx/include/optional index 9d8e8c403f772..b68126bd2b9de 100644 --- a/libcxx/include/optional +++ b/libcxx/include/optional @@ -617,7 +617,6 @@ struct __optional_iterator_aliases< _Tp, __enable_if_t::value && (is_function<__libcpp_remove_reference_t<_Tp>>::value || is_unbounded_array<__libcpp_remove_reference_t<_Tp>>::value))> > { -public: using __pointer _LIBCPP_NODEBUG = std::add_pointer_t<_Tp>; using __const_pointer _LIBCPP_NODEBUG = std::add_pointer_t; # ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL