Skip to content

Commit 1c51886

Browse files
authored
[libc++] Implement P3168R2: Give optional range support (#149441)
Resolves #105430 - Implement all required pieces of P3168R2 - Leverage existing `wrap_iter` and `bounded_iter` classes to implement the `optional` regular and hardened iterator type, respectively - Update documentation to match
1 parent 4ab1468 commit 1c51886

File tree

15 files changed

+361
-27
lines changed

15 files changed

+361
-27
lines changed

libcxx/cmake/caches/Generic-hardening-mode-fast-with-abi-breaks.cmake

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@ set(_defines
55
_LIBCPP_ABI_BOUNDED_ITERATORS_IN_VECTOR
66
_LIBCPP_ABI_BOUNDED_UNIQUE_PTR
77
_LIBCPP_ABI_BOUNDED_ITERATORS_IN_STD_ARRAY
8+
_LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
89
)
910
set(LIBCXX_ABI_DEFINES "${_defines}" CACHE STRING "")

libcxx/docs/FeatureTestMacroTable.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -480,7 +480,7 @@ Status
480480
---------------------------------------------------------- -----------------
481481
``__cpp_lib_not_fn`` ``202306L``
482482
---------------------------------------------------------- -----------------
483-
``__cpp_lib_optional_range_support`` *unimplemented*
483+
``__cpp_lib_optional_range_support`` ``202406L``
484484
---------------------------------------------------------- -----------------
485485
``__cpp_lib_out_ptr`` ``202311L``
486486
---------------------------------------------------------- -----------------

libcxx/docs/ReleaseNotes/22.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ Implemented Papers
3939
------------------
4040

4141
- P2321R2: ``zip`` (`Github <https://github.com/llvm/llvm-project/issues/105169>`__) (The paper is partially implemented. ``zip_transform_view`` is implemented in this release)
42+
- P3168R2: Give ``std::optional`` Range Support (`Github <https://github.com/llvm/llvm-project/issues/105430>`__)
4243

4344
Improvements and New Features
4445
-----------------------------

libcxx/docs/Status/Cxx2cPapers.csv

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@
6666
"`P2747R2 <https://wg21.link/P2747R2>`__","``constexpr`` placement new","2024-06 (St. Louis)","|Complete|","20",""
6767
"`P2997R1 <https://wg21.link/P2997R1>`__","Removing the common reference requirement from the indirectly invocable concepts","2024-06 (St. Louis)","|Complete|","19","Implemented as a DR against C++20. (MSVC STL and libstdc++ will do the same.)"
6868
"`P2389R2 <https://wg21.link/P2389R2>`__","``dextents`` Index Type Parameter","2024-06 (St. Louis)","|Complete|","19",""
69-
"`P3168R2 <https://wg21.link/P3168R2>`__","Give ``std::optional`` Range Support","2024-06 (St. Louis)","","",""
69+
"`P3168R2 <https://wg21.link/P3168R2>`__","Give ``std::optional`` Range Support","2024-06 (St. Louis)","|Complete|","22",""
7070
"`P3217R0 <https://wg21.link/P3217R0>`__","Adjoints to 'Enabling list-initialization for algorithms': find_last","2024-06 (St. Louis)","","",""
7171
"`P2985R0 <https://wg21.link/P2985R0>`__","A type trait for detecting virtual base classes","2024-06 (St. Louis)","|Complete|","20",""
7272
"`P0843R14 <https://wg21.link/P0843R14>`__","``inplace_vector``","2024-06 (St. Louis)","","",""

libcxx/include/__iterator/wrap_iter.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,8 @@ class __wrap_iter {
117117
friend class span;
118118
template <class _Tp, size_t _Size>
119119
friend struct array;
120+
template <class _Tp>
121+
friend class optional;
120122
};
121123

122124
template <class _Iter1>

libcxx/include/optional

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@ namespace std {
2020
template <class T>
2121
class optional;
2222
23+
template<class T>
24+
constexpr bool ranges::enable_view<optional<T>> = true;
25+
template<class T>
26+
constexpr auto format_kind<optional<T>> = range_format::disabled;
27+
2328
template<class T>
2429
concept is-derived-from-optional = requires(const T& t) { // exposition only
2530
[]<class U>(const optional<U>&){ }(t);
@@ -102,6 +107,8 @@ namespace std {
102107
class optional {
103108
public:
104109
using value_type = T;
110+
using iterator = implementation-defined; // see [optional.iterators]
111+
using const_iterator = implementation-defined; // see [optional.iterators]
105112
106113
// [optional.ctor], constructors
107114
constexpr optional() noexcept;
@@ -135,6 +142,12 @@ namespace std {
135142
// [optional.swap], swap
136143
void swap(optional &) noexcept(see below ); // constexpr in C++20
137144
145+
// [optional.iterators], iterator support
146+
constexpr iterator begin() noexcept;
147+
constexpr const_iterator begin() const noexcept;
148+
constexpr iterator end() noexcept;
149+
constexpr const_iterator end() const noexcept;
150+
138151
// [optional.observe], observers
139152
constexpr T const *operator->() const noexcept;
140153
constexpr T *operator->() noexcept;
@@ -186,13 +199,18 @@ namespace std {
186199
# include <__compare/three_way_comparable.h>
187200
# include <__concepts/invocable.h>
188201
# include <__config>
202+
# include <__cstddef/ptrdiff_t.h>
189203
# include <__exception/exception.h>
204+
# include <__format/range_format.h>
190205
# include <__functional/hash.h>
191206
# include <__functional/invoke.h>
192207
# include <__functional/unary_function.h>
193208
# include <__fwd/functional.h>
209+
# include <__iterator/bounded_iter.h>
210+
# include <__iterator/wrap_iter.h>
194211
# include <__memory/addressof.h>
195212
# include <__memory/construct_at.h>
213+
# include <__ranges/enable_view.h>
196214
# include <__tuple/sfinae_helpers.h>
197215
# include <__type_traits/add_pointer.h>
198216
# include <__type_traits/conditional.h>
@@ -207,6 +225,7 @@ namespace std {
207225
# include <__type_traits/is_convertible.h>
208226
# include <__type_traits/is_core_convertible.h>
209227
# include <__type_traits/is_destructible.h>
228+
# include <__type_traits/is_function.h>
210229
# include <__type_traits/is_nothrow_assignable.h>
211230
# include <__type_traits/is_nothrow_constructible.h>
212231
# include <__type_traits/is_object.h>
@@ -219,6 +238,7 @@ namespace std {
219238
# include <__type_traits/is_trivially_constructible.h>
220239
# include <__type_traits/is_trivially_destructible.h>
221240
# include <__type_traits/is_trivially_relocatable.h>
241+
# include <__type_traits/is_unbounded_array.h>
222242
# include <__type_traits/negation.h>
223243
# include <__type_traits/remove_const.h>
224244
# include <__type_traits/remove_cv.h>
@@ -567,6 +587,14 @@ using __optional_sfinae_assign_base_t _LIBCPP_NODEBUG =
567587
template <class _Tp>
568588
class optional;
569589

590+
# if _LIBCPP_STD_VER >= 26
591+
template <class _Tp>
592+
constexpr bool ranges::enable_view<optional<_Tp>> = true;
593+
594+
template <class _Tp>
595+
constexpr range_format format_kind<optional<_Tp>> = range_format::disabled;
596+
# endif
597+
570598
# if _LIBCPP_STD_VER >= 20
571599

572600
template <class _Tp>
@@ -586,9 +614,21 @@ class _LIBCPP_DECLSPEC_EMPTY_BASES optional
586614
private __optional_sfinae_assign_base_t<_Tp> {
587615
using __base _LIBCPP_NODEBUG = __optional_move_assign_base<_Tp>;
588616

617+
using __pointer _LIBCPP_NODEBUG = std::add_pointer_t<_Tp>;
618+
using __const_pointer _LIBCPP_NODEBUG = std::add_pointer_t<const _Tp>;
619+
589620
public:
590621
using value_type = _Tp;
591622

623+
# if _LIBCPP_STD_VER >= 26
624+
# ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
625+
using iterator = __bounded_iter<__wrap_iter<__pointer>>;
626+
using const_iterator = __bounded_iter<__wrap_iter<__const_pointer>>;
627+
# else
628+
using iterator = __wrap_iter<__pointer>;
629+
using const_iterator = __wrap_iter<__const_pointer>;
630+
# endif
631+
# endif
592632
using __trivially_relocatable _LIBCPP_NODEBUG =
593633
conditional_t<__libcpp_is_trivially_relocatable<_Tp>::value, optional, void>;
594634
using __replaceable _LIBCPP_NODEBUG = conditional_t<__is_replaceable_v<_Tp>, optional, void>;
@@ -792,6 +832,34 @@ public:
792832
}
793833
}
794834

835+
# if _LIBCPP_STD_VER >= 26
836+
// [optional.iterators], iterator support
837+
_LIBCPP_HIDE_FROM_ABI constexpr iterator begin() noexcept {
838+
# ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
839+
return std::__make_bounded_iter(
840+
std::__wrap_iter<__pointer>(std::addressof(this->__get())),
841+
std::__wrap_iter<__pointer>(std::addressof(this->__get())),
842+
std::__wrap_iter<__pointer>(std::addressof(this->__get()) + (this->has_value() ? 1 : 0)));
843+
# else
844+
return iterator(std::addressof(this->__get()));
845+
# endif
846+
}
847+
848+
_LIBCPP_HIDE_FROM_ABI constexpr const_iterator begin() const noexcept {
849+
# ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
850+
return std::__make_bounded_iter(
851+
std::__wrap_iter<__const_pointer>(std::addressof(this->__get())),
852+
std::__wrap_iter<__const_pointer>(std::addressof(this->__get())),
853+
std::__wrap_iter<__const_pointer>(std::addressof(this->__get()) + (this->has_value() ? 1 : 0)));
854+
# else
855+
return const_iterator(std::addressof(this->__get()));
856+
# endif
857+
}
858+
859+
_LIBCPP_HIDE_FROM_ABI constexpr iterator end() noexcept { return begin() + (this->has_value() ? 1 : 0); }
860+
_LIBCPP_HIDE_FROM_ABI constexpr const_iterator end() const noexcept { return begin() + (this->has_value() ? 1 : 0); }
861+
# endif
862+
795863
_LIBCPP_HIDE_FROM_ABI constexpr add_pointer_t<value_type const> operator->() const noexcept {
796864
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator-> called on a disengaged value");
797865
return std::addressof(this->__get());

libcxx/include/version

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -585,7 +585,7 @@ __cpp_lib_void_t 201411L <type_traits>
585585
# define __cpp_lib_mdspan 202406L
586586
# undef __cpp_lib_not_fn
587587
# define __cpp_lib_not_fn 202306L
588-
// # define __cpp_lib_optional_range_support 202406L
588+
# define __cpp_lib_optional_range_support 202406L
589589
# undef __cpp_lib_out_ptr
590590
# define __cpp_lib_out_ptr 202311L
591591
// # define __cpp_lib_philox_engine 202406L

libcxx/modules/std/optional.inc

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,23 @@
1010
export namespace std {
1111
// [optional.optional], class template optional
1212
using std::optional;
13-
13+
#if _LIBCPP_STD_VER >= 26
14+
// [optional.iterators], iterator support
15+
namespace ranges {
16+
using std::ranges::enable_view;
17+
}
18+
#endif
1419
// [optional.nullopt], no-value state indicator
1520
using std::nullopt;
1621
using std::nullopt_t;
1722

1823
// [optional.bad.access], class bad_optional_access
1924
using std::bad_optional_access;
2025

26+
#if _LIBCPP_STD_VER >= 26
27+
using std::format_kind;
28+
#endif
29+
2130
// [optional.relops], relational operators
2231
using std::operator==;
2332
using std::operator!=;
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
// REQUIRES: std-at-least-c++26
10+
11+
// <optional>
12+
13+
// template <class T> class optional::iterator;
14+
// template <class T> class optional::const_iterator;
15+
16+
#include <optional>
17+
18+
template <typename T>
19+
concept has_iterator_aliases = requires {
20+
typename T::iterator;
21+
typename T::const_iterator;
22+
};
23+
24+
static_assert(has_iterator_aliases<std::optional<int>>);
25+
static_assert(has_iterator_aliases<std::optional<const int>>);
26+
27+
// TODO: Uncomment these once P2988R12 is implemented, as they would be testing optional<T&>
28+
29+
// static_assert(!has_iterator_aliases<std::optional<int (&)[]>>);
30+
// static_assert(!has_iterator_aliases<std::optional<void (&)(int, char)>>);

libcxx/test/std/language.support/support.limits/support.limits.general/optional.version.compile.pass.cpp

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -146,17 +146,11 @@
146146
# error "__cpp_lib_optional should have the value 202110L in c++26"
147147
# endif
148148

149-
# if !defined(_LIBCPP_VERSION)
150-
# ifndef __cpp_lib_optional_range_support
151-
# error "__cpp_lib_optional_range_support should be defined in c++26"
152-
# endif
153-
# if __cpp_lib_optional_range_support != 202406L
154-
# error "__cpp_lib_optional_range_support should have the value 202406L in c++26"
155-
# endif
156-
# else
157-
# ifdef __cpp_lib_optional_range_support
158-
# error "__cpp_lib_optional_range_support should not be defined because it is unimplemented in libc++!"
159-
# endif
149+
# ifndef __cpp_lib_optional_range_support
150+
# error "__cpp_lib_optional_range_support should be defined in c++26"
151+
# endif
152+
# if __cpp_lib_optional_range_support != 202406L
153+
# error "__cpp_lib_optional_range_support should have the value 202406L in c++26"
160154
# endif
161155

162156
#endif // TEST_STD_VER > 23

0 commit comments

Comments
 (0)