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/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/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) diff --git a/libcxx/docs/Status/Cxx2cPapers.csv b/libcxx/docs/Status/Cxx2cPapers.csv index febb0c176f9c4..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)","","","" +"`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/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..f354005d8c772 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; @@ -186,13 +199,18 @@ 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/bounded_iter.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> @@ -567,6 +585,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 +612,22 @@ 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 + 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 = conditional_t<__libcpp_is_trivially_relocatable<_Tp>::value, optional, void>; @@ -792,6 +832,34 @@ public: } } +# if _LIBCPP_STD_VER >= 26 + // [optional.iterators], iterator support + _LIBCPP_HIDE_FROM_ABI constexpr iterator begin() noexcept { +# ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL + return std::__make_bounded_iter( + std::__wrap_iter(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); } + _LIBCPP_HIDE_FROM_ABI constexpr const_iterator end() const noexcept { return begin() + (this->has_value() ? 1 : 0); } +# endif + _LIBCPP_HIDE_FROM_ABI constexpr add_pointer_t operator->() const noexcept { _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator-> called on a disengaged value"); return std::addressof(this->__get()); 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/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/optional.iterator/begin.pass.cpp b/libcxx/test/std/utilities/optional/optional.iterator/begin.pass.cpp new file mode 100644 index 0000000000000..058785080640e --- /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 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]); + 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..97d9635b1332b --- /dev/null +++ b/libcxx/test/std/utilities/optional/optional.iterator/end.pass.cpp @@ -0,0 +1,73 @@ +//===----------------------------------------------------------------------===// +// +// 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() is marked noexcept + assert(noexcept(unengaged.end())); + assert(noexcept(unengaged2.end())); + } + + { // 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()); +} 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",