[libcxx-commits] [libcxx] 1c51886 - [libc++] Implement P3168R2: Give optional range support (#149441)

via libcxx-commits libcxx-commits at lists.llvm.org
Mon Aug 18 08:04:48 PDT 2025


Author: William Tran-Viet
Date: 2025-08-18T18:04:45+03:00
New Revision: 1c5188692036c51123ae78e9208d5a375d28f74a

URL: https://github.com/llvm/llvm-project/commit/1c5188692036c51123ae78e9208d5a375d28f74a
DIFF: https://github.com/llvm/llvm-project/commit/1c5188692036c51123ae78e9208d5a375d28f74a.diff

LOG: [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

Added: 
    libcxx/test/libcxx/utilities/optional/optional.iterator/iterator.compile.pass.cpp
    libcxx/test/std/utilities/optional/optional.iterator/begin.pass.cpp
    libcxx/test/std/utilities/optional/optional.iterator/end.pass.cpp
    libcxx/test/std/utilities/optional/optional.iterator/iterator.pass.cpp

Modified: 
    libcxx/cmake/caches/Generic-hardening-mode-fast-with-abi-breaks.cmake
    libcxx/docs/FeatureTestMacroTable.rst
    libcxx/docs/ReleaseNotes/22.rst
    libcxx/docs/Status/Cxx2cPapers.csv
    libcxx/include/__iterator/wrap_iter.h
    libcxx/include/optional
    libcxx/include/version
    libcxx/modules/std/optional.inc
    libcxx/test/std/language.support/support.limits/support.limits.general/optional.version.compile.pass.cpp
    libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
    libcxx/utils/generate_feature_test_macro_components.py

Removed: 
    


################################################################################
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 a36848ebd24b4..358889d8dbc37 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 191dab6b77564..f28babf548fe4 100644
--- a/libcxx/docs/ReleaseNotes/22.rst
+++ b/libcxx/docs/ReleaseNotes/22.rst
@@ -39,6 +39,7 @@ Implemented Papers
 ------------------
 
 - P2321R2: ``zip`` (`Github <https://github.com/llvm/llvm-project/issues/105169>`__) (The paper is partially implemented. ``zip_transform_view`` is implemented in this release)
+- P3168R2: Give ``std::optional`` Range Support (`Github <https://github.com/llvm/llvm-project/issues/105430>`__)
 
 Improvements and New Features
 -----------------------------

diff  --git a/libcxx/docs/Status/Cxx2cPapers.csv b/libcxx/docs/Status/Cxx2cPapers.csv
index e8b0c9559f40b..3b8b2b7ad0b3f 100644
--- a/libcxx/docs/Status/Cxx2cPapers.csv
+++ b/libcxx/docs/Status/Cxx2cPapers.csv
@@ -66,7 +66,7 @@
 "`P2747R2 <https://wg21.link/P2747R2>`__","``constexpr`` placement new","2024-06 (St. Louis)","|Complete|","20",""
 "`P2997R1 <https://wg21.link/P2997R1>`__","Removing the common reference requirement from the indirectly invocable concepts","2024-06 (St. Louis)","|Complete|","19","Implemented as a DR against C++20. (MSVC STL and libstdc++ will do the same.)"
 "`P2389R2 <https://wg21.link/P2389R2>`__","``dextents`` Index Type Parameter","2024-06 (St. Louis)","|Complete|","19",""
-"`P3168R2 <https://wg21.link/P3168R2>`__","Give ``std::optional`` Range Support","2024-06 (St. Louis)","","",""
+"`P3168R2 <https://wg21.link/P3168R2>`__","Give ``std::optional`` Range Support","2024-06 (St. Louis)","|Complete|","22",""
 "`P3217R0 <https://wg21.link/P3217R0>`__","Adjoints to 'Enabling list-initialization for algorithms': find_last","2024-06 (St. Louis)","","",""
 "`P2985R0 <https://wg21.link/P2985R0>`__","A type trait for detecting virtual base classes","2024-06 (St. Louis)","|Complete|","20",""
 "`P0843R14 <https://wg21.link/P0843R14>`__","``inplace_vector``","2024-06 (St. Louis)","","",""

diff  --git a/libcxx/include/__iterator/wrap_iter.h b/libcxx/include/__iterator/wrap_iter.h
index 2b5bc489dd44c..7610586ddecbb 100644
--- a/libcxx/include/__iterator/wrap_iter.h
+++ b/libcxx/include/__iterator/wrap_iter.h
@@ -117,6 +117,8 @@ class __wrap_iter {
   friend class span;
   template <class _Tp, size_t _Size>
   friend struct array;
+  template <class _Tp>
+  friend class optional;
 };
 
 template <class _Iter1>

diff  --git a/libcxx/include/optional b/libcxx/include/optional
index e81bff50daad6..39fcaa2c2ec18 100644
--- a/libcxx/include/optional
+++ b/libcxx/include/optional
@@ -20,6 +20,11 @@ namespace std {
   template <class T>
     class optional;
 
+  template<class T>
+   constexpr bool ranges::enable_view<optional<T>> = true;
+  template<class T>
+   constexpr auto format_kind<optional<T>> = range_format::disabled;
+
   template<class T>
     concept is-derived-from-optional = requires(const T& t) {       // exposition only
       []<class U>(const optional<U>&){ }(t);
@@ -102,6 +107,8 @@ namespace std {
   class optional {
   public:
     using value_type = T;
+    using iterator               = implementation-defined; // see [optional.iterators]
+    using const_iterator         = implementation-defined; // see [optional.iterators]
 
     // [optional.ctor], constructors
     constexpr optional() noexcept;
@@ -135,6 +142,12 @@ namespace std {
     // [optional.swap], swap
     void swap(optional &) noexcept(see below ); // constexpr in C++20
 
+    // [optional.iterators], iterator support
+    constexpr iterator begin() noexcept;
+    constexpr const_iterator begin() const noexcept;
+    constexpr iterator end() noexcept;
+    constexpr const_iterator end() const noexcept;
+
     // [optional.observe], observers
     constexpr T const *operator->() const noexcept;
     constexpr T *operator->() noexcept;
@@ -186,13 +199,18 @@ namespace std {
 #  include <__compare/three_way_comparable.h>
 #  include <__concepts/invocable.h>
 #  include <__config>
+#  include <__cstddef/ptr
diff _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>
@@ -207,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>
@@ -219,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>
@@ -567,6 +587,14 @@ using __optional_sfinae_assign_base_t _LIBCPP_NODEBUG =
 template <class _Tp>
 class optional;
 
+#    if _LIBCPP_STD_VER >= 26
+template <class _Tp>
+constexpr bool ranges::enable_view<optional<_Tp>> = true;
+
+template <class _Tp>
+constexpr range_format format_kind<optional<_Tp>> = range_format::disabled;
+#    endif
+
 #    if _LIBCPP_STD_VER >= 20
 
 template <class _Tp>
@@ -586,9 +614,21 @@ class _LIBCPP_DECLSPEC_EMPTY_BASES optional
       private __optional_sfinae_assign_base_t<_Tp> {
   using __base _LIBCPP_NODEBUG = __optional_move_assign_base<_Tp>;
 
+  using __pointer _LIBCPP_NODEBUG       = std::add_pointer_t<_Tp>;
+  using __const_pointer _LIBCPP_NODEBUG = std::add_pointer_t<const _Tp>;
+
 public:
   using value_type = _Tp;
 
+#    if _LIBCPP_STD_VER >= 26
+#      ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
+  using iterator       = __bounded_iter<__wrap_iter<__pointer>>;
+  using const_iterator = __bounded_iter<__wrap_iter<__const_pointer>>;
+#      else
+  using iterator       = __wrap_iter<__pointer>;
+  using const_iterator = __wrap_iter<__const_pointer>;
+#      endif
+#    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>;
@@ -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<__pointer>(std::addressof(this->__get())),
+        std::__wrap_iter<__pointer>(std::addressof(this->__get())),
+        std::__wrap_iter<__pointer>(std::addressof(this->__get()) + (this->has_value() ? 1 : 0)));
+#      else
+    return iterator(std::addressof(this->__get()));
+#      endif
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr const_iterator begin() const noexcept {
+#      ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
+    return std::__make_bounded_iter(
+        std::__wrap_iter<__const_pointer>(std::addressof(this->__get())),
+        std::__wrap_iter<__const_pointer>(std::addressof(this->__get())),
+        std::__wrap_iter<__const_pointer>(std::addressof(this->__get()) + (this->has_value() ? 1 : 0)));
+#      else
+    return const_iterator(std::addressof(this->__get()));
+#      endif
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr iterator end() noexcept { return begin() + (this->has_value() ? 1 : 0); }
+  _LIBCPP_HIDE_FROM_ABI constexpr const_iterator end() const noexcept { return begin() + (this->has_value() ? 1 : 0); }
+#    endif
+
   _LIBCPP_HIDE_FROM_ABI constexpr add_pointer_t<value_type const> operator->() const noexcept {
     _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator-> called on a disengaged value");
     return std::addressof(this->__get());

diff  --git a/libcxx/include/version b/libcxx/include/version
index aae9277a7dfc6..16917a3bd9ddd 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -585,7 +585,7 @@ __cpp_lib_void_t                                        201411L <type_traits>
 # define __cpp_lib_mdspan                               202406L
 # undef  __cpp_lib_not_fn
 # define __cpp_lib_not_fn                               202306L
-// # define __cpp_lib_optional_range_support               202406L
+# define __cpp_lib_optional_range_support               202406L
 # undef  __cpp_lib_out_ptr
 # define __cpp_lib_out_ptr                              202311L
 // # define __cpp_lib_philox_engine                        202406L

diff  --git a/libcxx/modules/std/optional.inc b/libcxx/modules/std/optional.inc
index 0f812bc0e24a4..9ee51117277ce 100644
--- a/libcxx/modules/std/optional.inc
+++ b/libcxx/modules/std/optional.inc
@@ -10,7 +10,12 @@
 export namespace std {
   // [optional.optional], class template optional
   using std::optional;
-
+#if _LIBCPP_STD_VER >= 26
+  // [optional.iterators], iterator support
+  namespace ranges {
+    using std::ranges::enable_view;
+  }
+#endif
   // [optional.nullopt], no-value state indicator
   using std::nullopt;
   using std::nullopt_t;
@@ -18,6 +23,10 @@ export namespace std {
   // [optional.bad.access], class bad_optional_access
   using std::bad_optional_access;
 
+#if _LIBCPP_STD_VER >= 26
+  using std::format_kind;
+#endif
+
   // [optional.relops], relational operators
   using std::operator==;
   using std::operator!=;

diff  --git a/libcxx/test/libcxx/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
+
+// <optional>
+
+// template <class T> class optional::iterator;
+// template <class T> class optional::const_iterator;
+
+#include <optional>
+
+template <typename T>
+concept has_iterator_aliases = requires {
+  typename T::iterator;
+  typename T::const_iterator;
+};
+
+static_assert(has_iterator_aliases<std::optional<int>>);
+static_assert(has_iterator_aliases<std::optional<const int>>);
+
+// TODO: Uncomment these once P2988R12 is implemented, as they would be testing optional<T&>
+
+// static_assert(!has_iterator_aliases<std::optional<int (&)[]>>);
+// static_assert(!has_iterator_aliases<std::optional<void (&)(int, char)>>);

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 ccdb1a8c11a0b..aca6290f5a4bf 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
@@ -146,17 +146,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 7bd8e8979e6f3..cde2f258b7732 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
@@ -7437,17 +7437,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..df95a8df3793f
--- /dev/null
+++ b/libcxx/test/std/utilities/optional/optional.iterator/begin.pass.cpp
@@ -0,0 +1,64 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// <optional>
+
+// constexpr iterator optional::begin() noexcept;
+// constexpr const_iterator optional::begin() const noexcept;
+
+#include <cassert>
+#include <iterator>
+#include <optional>
+#include <type_traits>
+#include <utility>
+
+template <typename T>
+constexpr bool test() {
+  std::optional<T> opt{T{}};
+
+  { // begin() is marked noexcept
+    static_assert(noexcept(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 = std::as_const(opt).begin();
+    assert(*iter1 == iter1[0]);
+    assert(*iter2 == iter2[0]);
+    assert(iter1 == opt.begin());
+    assert(iter2 == std::as_const(opt).begin());
+  }
+
+  { // Calling begin() multiple times on a disengaged optional returns the same iterator.
+    std::optional<T> 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;
+}
+
+constexpr bool tests() {
+  assert(test<int>());
+  assert(test<char>());
+  assert(test<const int>());
+  assert(test<const char>());
+  return true;
+}
+
+int main(int, char**) {
+  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..966c3e7441880
--- /dev/null
+++ b/libcxx/test/std/utilities/optional/optional.iterator/end.pass.cpp
@@ -0,0 +1,74 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// <optional>
+
+// constexpr iterator optional::end() noexcept;
+// constexpr const_iterator optional::end() const noexcept;
+
+#include <cassert>
+#include <iterator>
+#include <optional>
+#include <ranges>
+#include <utility>
+
+template <typename T>
+constexpr bool test() {
+  std::optional<T> disengaged{std::nullopt};
+
+  { // end() is marked noexcept
+    static_assert(noexcept(disengaged.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 = std::as_const(disengaged).end();
+
+    assert(it == disengaged.begin());
+    assert(disengaged.begin() == it);
+    assert(it == disengaged.end());
+
+    assert(it2 == std::as_const(disengaged).begin());
+    assert(std::as_const(disengaged).begin() == it2);
+    assert(it2 == std::as_const(disengaged).end());
+  }
+
+  std::optional<T> engaged{T{}};
+
+  { // end() != begin() if the optional is engaged
+    auto it  = engaged.end();
+    auto it2 = std::as_const(engaged).end();
+
+    assert(it != engaged.begin());
+    assert(engaged.begin() != it);
+
+    assert(it2 != std::as_const(engaged).begin());
+    assert(std::as_const(engaged).begin() != it2);
+  }
+
+  return true;
+}
+
+constexpr bool tests() {
+  assert(test<int>());
+  assert(test<char>());
+  assert(test<const int>());
+  assert(test<const char>());
+
+  return true;
+}
+
+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
new file mode 100644
index 0000000000000..1203290a0290a
--- /dev/null
+++ b/libcxx/test/std/utilities/optional/optional.iterator/iterator.pass.cpp
@@ -0,0 +1,98 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// <optional>
+
+// template <class T> class optional::iterator;
+// template <class T> class optional::const_iterator;
+
+#include <cassert>
+#include <iterator>
+#include <optional>
+#include <ranges>
+#include <type_traits>
+#include <utility>
+
+template <typename T, T __val>
+constexpr bool test() {
+  std::optional<T> opt{__val};
+
+  { // Dereferencing an iterator of an engaged optional will return the same value that the optional holds.
+    auto it  = opt.begin();
+    auto it2 = std::as_const(opt).begin();
+    assert(*it == *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 = std::as_const(opt).begin();
+    assert(std::contiguous_iterator<decltype(it)>);
+    assert(std::contiguous_iterator<decltype(it2)>);
+
+    assert(std::random_access_iterator<decltype(it)>);
+    assert(std::random_access_iterator<decltype(it2)>);
+  }
+
+  { // const_iterator::value_type == std::remove_cv_t<T>, const_iterator::reference == const T&, iterator::value_type = std::remove_cv_t<T>, iterator::reference == T&
+    auto it  = opt.begin();
+    auto it2 = std::as_const(opt).begin();
+    assert((std::is_same_v<typename decltype(it)::value_type, std::remove_cv_t<T>>));
+    assert((std::is_same_v<typename decltype(it)::reference, T&>));
+    assert((std::is_same_v<typename decltype(it2)::value_type, std::remove_cv_t<T>>));
+    assert((std::is_same_v<typename decltype(it2)::reference, const T&>));
+  }
+
+  { // std::ranges::size for an engaged optional<T> == 1, disengaged optional<T> == 0
+    const std::optional<T> disengaged{std::nullopt};
+    std::optional<T> disengaged2{std::nullopt};
+    assert(std::ranges::size(opt) == 1);
+    assert(std::ranges::size(std::as_const(opt)) == 1);
+
+    assert(std::ranges::size(disengaged) == 0);
+    assert(std::ranges::size(disengaged2) == 0);
+  }
+
+  { // std::ranges::enable_view<optional<T>> == true, and std::format_kind<optional<T>> == true
+    static_assert(std::ranges::enable_view<std::optional<T>> == true);
+    static_assert(std::format_kind<std::optional<T>> == std::range_format::disabled);
+  }
+
+  // An optional with value that is reset will have a begin() == end(), then when it is reassigned a value,
+  // begin() != end(), and *begin() will contain the new value.
+  {
+    std::optional<T> val{__val};
+    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<int, 1>()));
+  assert((test<char, 'a'>()));
+  assert((test<bool, true>()));
+  assert((test<const int, 2>()));
+  assert((test<const char, 'b'>()));
+
+  return true;
+}
+
+int main(int, char**) {
+  assert(tests());
+  static_assert(tests());
+
+  return 0;
+}

diff  --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index d9317e00e3f4a..8d57a07b8836b 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -1012,7 +1012,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",


        


More information about the libcxx-commits mailing list