[libcxx-commits] [libcxx] [libc++] Remove `optional::iterator` from experimental mode (PR #203782)
William Tran-Viet via libcxx-commits
libcxx-commits at lists.llvm.org
Fri Oct 2 18:26:37 PDT 2026
https://github.com/smallp-o-p updated https://github.com/llvm/llvm-project/pull/203782
>From 0daa357edac4824cb4d025e7e2a3c780e461080b Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Sun, 14 Jun 2026 14:49:29 -0400
Subject: [PATCH 1/8] Remove `optional::iterator` from experimental mode
---
libcxx/docs/ReleaseNotes/23.rst | 1 +
libcxx/include/__configuration/experimental.h | 1 -
libcxx/include/optional | 37 +++++++++----------
libcxx/include/version | 4 +-
libcxx/modules/std/optional.inc | 4 +-
.../fexperimental-library.compile.pass.cpp | 4 --
.../utilities/optional/nodiscard.verify.cpp | 4 +-
.../optional.version.compile.pass.cpp | 16 +++-----
.../version.version.compile.pass.cpp | 16 +++-----
.../generate_feature_test_macro_components.py | 2 -
10 files changed, 32 insertions(+), 57 deletions(-)
diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index 7823c2db39867c..726e576ef40382 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -65,6 +65,7 @@ Improvements and New Features
``"std::visit: variant is valueless"``, ``"std::get: variant is valueless"``, or
``"std::get: wrong alternative for variant"``. The standard only requires ``what()`` to return an
unspecified non-null string, so user code that does not match on the exact message remains correct.
+- ``optional::iterator`` is no longer experimental.
Deprecations and Removals
-------------------------
diff --git a/libcxx/include/__configuration/experimental.h b/libcxx/include/__configuration/experimental.h
index bb38d8297c63db..d14df3e5175f35 100644
--- a/libcxx/include/__configuration/experimental.h
+++ b/libcxx/include/__configuration/experimental.h
@@ -33,6 +33,5 @@
#define _LIBCPP_HAS_EXPERIMENTAL_TZDB _LIBCPP_HAS_EXPERIMENTAL_LIBRARY
#define _LIBCPP_HAS_EXPERIMENTAL_SYNCSTREAM _LIBCPP_HAS_EXPERIMENTAL_LIBRARY
#define _LIBCPP_HAS_EXPERIMENTAL_HARDENING_OBSERVE_SEMANTIC _LIBCPP_HAS_EXPERIMENTAL_LIBRARY
-#define _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR _LIBCPP_HAS_EXPERIMENTAL_LIBRARY
#endif // _LIBCPP___CONFIGURATION_EXPERIMENTAL_H
diff --git a/libcxx/include/optional b/libcxx/include/optional
index 66995435b5109b..43e8511a957894 100644
--- a/libcxx/include/optional
+++ b/libcxx/include/optional
@@ -737,7 +737,7 @@ using __optional_sfinae_assign_base_t _LIBCPP_NODEBUG =
template <class _Tp>
class optional;
-# if _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
+# if _LIBCPP_STD_VER >= 26
template <class _Tp>
constexpr bool ranges::enable_view<optional<_Tp>> = true;
@@ -780,8 +780,6 @@ struct __optional_iterator_base<_Tp&> : __optional_storage_base<_Tp&> {
using __optional_storage_base<_Tp&>::__optional_storage_base;
};
-# if _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
-
template <class _Tp>
requires is_object_v<_Tp>
struct __optional_iterator_base<_Tp> : __optional_move_assign_base<_Tp> {
@@ -792,33 +790,33 @@ private:
public:
using __optional_move_assign_base<_Tp>::__optional_move_assign_base;
-# ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
+# ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
using iterator = __bounded_iter<__pointer>;
using const_iterator = __bounded_iter<__const_pointer>;
-# else
+# else
using iterator = __capacity_aware_iterator<__pointer, optional<_Tp>, 1>;
using const_iterator = __capacity_aware_iterator<__const_pointer, optional<_Tp>, 1>;
-# endif
+# endif
// [optional.iterators], iterator support
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator begin() noexcept {
auto* __ptr = std::addressof(this->__get());
-# ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
+# ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
return std::__make_bounded_iter(__ptr, __ptr, __ptr + (this->has_value() ? 1 : 0));
-# else
+# else
return std::__make_capacity_aware_iterator<__pointer, optional<_Tp>, 1>(__ptr);
-# endif
+# endif
}
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const_iterator begin() const noexcept {
auto* __ptr = std::addressof(this->__get());
-# ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
+# ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
return std::__make_bounded_iter(__ptr, __ptr, __ptr + (this->has_value() ? 1 : 0));
-# else
+# else
return std::__make_capacity_aware_iterator<__const_pointer, optional<_Tp>, 1>(__ptr);
-# endif
+# endif
}
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator end() noexcept {
@@ -838,22 +836,22 @@ private:
public:
using __optional_storage_base<_Tp&>::__optional_storage_base;
-# ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
+# ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
using iterator = __bounded_iter<__pointer>;
-# else
+# else
using iterator = __capacity_aware_iterator<__pointer, optional<_Tp&>, 1>;
-# endif
+# endif
// [optional.ref.iterators], iterator support
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const noexcept {
auto* __ptr = this->has_value() ? std::addressof(this->__get()) : nullptr;
-# ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
+# ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
return std::__make_bounded_iter(__ptr, __ptr, __ptr + (this->has_value() ? 1 : 0));
-# else
+# else
return std::__make_capacity_aware_iterator<__pointer, optional<_Tp&>, 1>(__ptr);
-# endif
+# endif
}
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const noexcept {
@@ -861,8 +859,7 @@ public:
}
};
-# endif // _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
-# endif // _LIBCPP_STD_VER >= 26
+# endif // _LIBCPP_STD_VER >= 26
template <class _Tp>
class _LIBCPP_DECLSPEC_EMPTY_BASES optional
diff --git a/libcxx/include/version b/libcxx/include/version
index 7f2dc9e4b72ab1..9d3d7395f98e59 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -611,9 +611,7 @@ __cpp_lib_void_t 201411L <type_traits>
# define __cpp_lib_not_fn 202306L
# undef __cpp_lib_optional
# define __cpp_lib_optional 202506L
-# if _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
-# define __cpp_lib_optional_range_support 202406L
-# endif
+# 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 8196a8dde577ce..88de0bb4db12b9 100644
--- a/libcxx/modules/std/optional.inc
+++ b/libcxx/modules/std/optional.inc
@@ -10,7 +10,7 @@
export namespace std {
// [optional.optional], class template optional
using std::optional;
-#if _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
+#if _LIBCPP_STD_VER >= 26
// [optional.iterators], iterator support
namespace ranges {
using std::ranges::enable_borrowed_range;
@@ -24,7 +24,7 @@ export namespace std {
// [optional.bad.access], class bad_optional_access
using std::bad_optional_access;
-#if _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
+#if _LIBCPP_STD_VER >= 26
using std::format_kind;
#endif
diff --git a/libcxx/test/libcxx/experimental/fexperimental-library.compile.pass.cpp b/libcxx/test/libcxx/experimental/fexperimental-library.compile.pass.cpp
index 82fd1ad4d9a16b..479b46a39e963c 100644
--- a/libcxx/test/libcxx/experimental/fexperimental-library.compile.pass.cpp
+++ b/libcxx/test/libcxx/experimental/fexperimental-library.compile.pass.cpp
@@ -16,10 +16,6 @@
#include <version>
-#if !_LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
-# error "-fexperimental-library should enable optional::iterator"
-#endif
-
#if !_LIBCPP_HAS_EXPERIMENTAL_PSTL
# error "-fexperimental-library should enable the PSTL"
#endif
diff --git a/libcxx/test/libcxx/utilities/optional/nodiscard.verify.cpp b/libcxx/test/libcxx/utilities/optional/nodiscard.verify.cpp
index 0ae368b4f87ce6..c49546cfdf4adb 100644
--- a/libcxx/test/libcxx/utilities/optional/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/utilities/optional/nodiscard.verify.cpp
@@ -32,7 +32,7 @@ void test() {
opt.has_value(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-#if TEST_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
+#if TEST_STD_VER >= 26
opt.begin(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
cOpt.begin(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
opt.end(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -92,10 +92,8 @@ void test() {
optRef.has_value(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-# if _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
optRef.begin(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
optRef.end(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-# endif
*optRef; // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
*cOptRef; // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
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 9850c20afec872..c4e652979a4e66 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 202506L in c++26"
# endif
-# if !defined(_LIBCPP_VERSION) || _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
-# 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 when the requirement '!defined(_LIBCPP_VERSION) || _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR' is not met!"
-# 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 d9c78b73f7e237..ac4e0003347a64 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
@@ -7586,17 +7586,11 @@
# error "__cpp_lib_optional should have the value 202506L in c++26"
# endif
-# if !defined(_LIBCPP_VERSION) || _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
-# 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 when the requirement '!defined(_LIBCPP_VERSION) || _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR' is not met!"
-# 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/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index d764a1f677ba6a..02e79ad7ca38ff 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -1039,8 +1039,6 @@ def add_version_header(tc):
"name": "__cpp_lib_optional_range_support",
"values": {"c++26": 202406}, # P3168R2 Give std::optional Range Support
"headers": ["optional"],
- "test_suite_guard": "!defined(_LIBCPP_VERSION) || _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR",
- "libcxx_guard": "_LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR",
},
{
"name": "__cpp_lib_out_ptr",
>From 3aff5a1dd1a74abe4ec54d669df3de0a72eedb36 Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Sun, 14 Jun 2026 14:54:46 -0400
Subject: [PATCH 2/8] Tests
---
.../libcxx/utilities/optional/nodiscard.iterator.verify.cpp | 2 --
.../optional/optional.iterator/iterator.compile.pass.cpp | 2 +-
.../std/utilities/optional/optional.iterator/begin.pass.cpp | 2 --
.../optional/optional.iterator/borrowed_range.compile.pass.cpp | 2 --
.../std/utilities/optional/optional.iterator/compare.pass.cpp | 1 -
.../test/std/utilities/optional/optional.iterator/end.pass.cpp | 2 --
.../std/utilities/optional/optional.iterator/iterator.pass.cpp | 2 --
libcxx/utils/libcxx/test/params.py | 1 -
8 files changed, 1 insertion(+), 13 deletions(-)
diff --git a/libcxx/test/libcxx/utilities/optional/nodiscard.iterator.verify.cpp b/libcxx/test/libcxx/utilities/optional/nodiscard.iterator.verify.cpp
index a3945000cbb77d..7fd96498870121 100644
--- a/libcxx/test/libcxx/utilities/optional/nodiscard.iterator.verify.cpp
+++ b/libcxx/test/libcxx/utilities/optional/nodiscard.iterator.verify.cpp
@@ -8,8 +8,6 @@
// REQUIRES: std-at-least-c++26
-// UNSUPPORTED: libcpp-has-no-experimental-optional-iterator
-
// <optional>
// Check that functions are marked [[nodiscard]]
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
index 265d39c97cb391..b12d421adef7db 100644
--- a/libcxx/test/libcxx/utilities/optional/optional.iterator/iterator.compile.pass.cpp
+++ b/libcxx/test/libcxx/utilities/optional/optional.iterator/iterator.compile.pass.cpp
@@ -10,7 +10,7 @@
// <optional>
-// UNSUPPORTED: libcpp-has-abi-bounded-iterators-in-optional, libcpp-has-no-experimental-optional-iterator
+// UNSUPPORTED: libcpp-has-abi-bounded-iterators-in-optional
// Ensure that a std::optional<T>::iterator can only be converted to a std::optional<T>::iterator.
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 7c75fe8b450769..81234525923a10 100644
--- a/libcxx/test/std/utilities/optional/optional.iterator/begin.pass.cpp
+++ b/libcxx/test/std/utilities/optional/optional.iterator/begin.pass.cpp
@@ -8,8 +8,6 @@
// REQUIRES: std-at-least-c++26
-// UNSUPPORTED: libcpp-has-no-experimental-optional-iterator
-
// <optional>
// constexpr iterator optional::begin() noexcept;
diff --git a/libcxx/test/std/utilities/optional/optional.iterator/borrowed_range.compile.pass.cpp b/libcxx/test/std/utilities/optional/optional.iterator/borrowed_range.compile.pass.cpp
index 65edbd54ff5b8b..a79d1d51a5b11c 100644
--- a/libcxx/test/std/utilities/optional/optional.iterator/borrowed_range.compile.pass.cpp
+++ b/libcxx/test/std/utilities/optional/optional.iterator/borrowed_range.compile.pass.cpp
@@ -8,8 +8,6 @@
// REQUIRES: std-at-least-c++26
-// UNSUPPORTED: libcpp-has-no-experimental-optional-iterator
-
// <optional>
// template <class T> class optional<T&>::iterator;
diff --git a/libcxx/test/std/utilities/optional/optional.iterator/compare.pass.cpp b/libcxx/test/std/utilities/optional/optional.iterator/compare.pass.cpp
index 4c48c54df32577..b3f778328c0cc9 100644
--- a/libcxx/test/std/utilities/optional/optional.iterator/compare.pass.cpp
+++ b/libcxx/test/std/utilities/optional/optional.iterator/compare.pass.cpp
@@ -7,7 +7,6 @@
//===----------------------------------------------------------------------===//
// REQUIRES: std-at-least-c++26
-// UNSUPPORTED: libcpp-has-no-experimental-optional-iterator
// <optional>
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 563f160092a382..c62c9fc7746d6a 100644
--- a/libcxx/test/std/utilities/optional/optional.iterator/end.pass.cpp
+++ b/libcxx/test/std/utilities/optional/optional.iterator/end.pass.cpp
@@ -8,8 +8,6 @@
// REQUIRES: std-at-least-c++26
-// UNSUPPORTED: libcpp-has-no-experimental-optional-iterator
-
// <optional>
// constexpr iterator optional::end() noexcept;
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 05b8b3572ef53e..996811d078b869 100644
--- a/libcxx/test/std/utilities/optional/optional.iterator/iterator.pass.cpp
+++ b/libcxx/test/std/utilities/optional/optional.iterator/iterator.pass.cpp
@@ -8,8 +8,6 @@
// REQUIRES: std-at-least-c++26
-// UNSUPPORTED: libcpp-has-no-experimental-optional-iterator
-
// <optional>
// template <class T> class optional::iterator;
diff --git a/libcxx/utils/libcxx/test/params.py b/libcxx/utils/libcxx/test/params.py
index d0bb38ab84df49..b0e0c48873eb33 100644
--- a/libcxx/utils/libcxx/test/params.py
+++ b/libcxx/utils/libcxx/test/params.py
@@ -362,7 +362,6 @@ def getSuitableClangTidy(cfg):
if experimental
else [
AddFeature("libcpp-has-no-incomplete-pstl"),
- AddFeature("libcpp-has-no-experimental-optional-iterator"),
AddFeature("libcpp-has-no-experimental-tzdb"),
AddFeature("libcpp-has-no-experimental-syncstream"),
AddFeature("libcpp-has-no-experimental-hardening-observe-semantic"),
>From c498aadfb57c8f76707a04b7ad181137129f4139 Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Fri, 2 Oct 2026 14:02:23 -0400
Subject: [PATCH 3/8] Reimplement optional::iterator for potentially 3 iterator
types
---
libcxx/docs/ReleaseNotes/24.rst | 1 +
libcxx/include/__optional/common.h | 4 +-
libcxx/include/__optional/optional.h | 67 ++++++++-----
libcxx/include/__optional/optional_ref.h | 51 ++++++----
.../assert.arithmetic.pass.cpp | 81 +++++++++++++++-
.../assert.bounded_iterator.pass.cpp | 95 +++++++++++++++++--
.../optional/optional.iterator/begin.pass.cpp | 7 +-
.../borrowed_range.compile.pass.cpp | 2 +
.../optional.iterator/compare.pass.cpp | 3 +-
.../optional/optional.iterator/end.pass.cpp | 8 +-
.../optional.iterator/iterator.pass.cpp | 6 +-
11 files changed, 268 insertions(+), 57 deletions(-)
diff --git a/libcxx/docs/ReleaseNotes/24.rst b/libcxx/docs/ReleaseNotes/24.rst
index 38d31eea96e4cf..d2fc16bcbef022 100644
--- a/libcxx/docs/ReleaseNotes/24.rst
+++ b/libcxx/docs/ReleaseNotes/24.rst
@@ -60,6 +60,7 @@ Improvements and New Features
-----------------------------
- ``basic_filebuf`` now supports files larger than 2GB on 32-bit AIX and 32-bit glibc targets.
+- ``optional::iterator`` is no longer experimental.
Deprecations and Removals
-------------------------
diff --git a/libcxx/include/__optional/common.h b/libcxx/include/__optional/common.h
index 26031f658a9042..8c759ab005cd86 100644
--- a/libcxx/include/__optional/common.h
+++ b/libcxx/include/__optional/common.h
@@ -72,7 +72,7 @@ template <class _Tp>
inline constexpr bool __is_valid_optional_contained_type = is_object_v<_Tp> || is_lvalue_reference_v<_Tp>;
# endif
-# if _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
+# if _LIBCPP_STD_VER >= 26
template <class _Tp>
constexpr bool ranges::enable_view<optional<_Tp>> = true;
@@ -83,7 +83,7 @@ constexpr range_format format_kind<optional<_Tp>> = range_format::disabled;
template <class _Tp>
constexpr bool ranges::enable_borrowed_range<optional<_Tp&>> = true;
-# endif // _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
+# endif // _LIBCPP_STD_VER >= 26
_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__optional/optional.h b/libcxx/include/__optional/optional.h
index 52f3bde864f905..bbd53528ccdd5c 100644
--- a/libcxx/include/__optional/optional.h
+++ b/libcxx/include/__optional/optional.h
@@ -16,6 +16,7 @@
#include <__fwd/optional.h>
#include <__iterator/bounded_iter.h>
#include <__iterator/capacity_aware_iterator.h>
+#include <__iterator/static_packed_bounded_iter.h>
#include <__memory/addressof.h>
#include <__memory/construct_at.h>
#include <__optional/common.h>
@@ -681,50 +682,68 @@ class _LIBCPP_DECLSPEC_EMPTY_BASES optional
using __base::reset;
-# if _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
+# if _LIBCPP_STD_VER >= 26
private:
using __pointer _LIBCPP_NODEBUG = add_pointer_t<_Tp>;
using __const_pointer _LIBCPP_NODEBUG = add_pointer_t<const _Tp>;
-public:
-# ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
- using iterator = __bounded_iter<__pointer>;
- using const_iterator = __bounded_iter<__const_pointer>;
+ template <bool = std::__range_fits_in_alignment(alignof(_Tp), 1)>
+ struct _LIBCPP_NODEBUG __iterator {
+ using __type _LIBCPP_NODEBUG = std::__static_packed_bounded_iterator<__pointer, 1>;
+ using __const_type _LIBCPP_NODEBUG = std::__static_packed_bounded_iterator<__const_pointer, 1>;
+
+ template <class _PtrType>
+ static auto __make(_PtrType __p, unsigned) noexcept {
+ return std::__make_static_packed_bounded_iter<_PtrType, 1>(__p, 0);
+ }
+ };
+
+ template <>
+ struct _LIBCPP_NODEBUG __iterator<false> {
+# if _LIBCPP_ABI_BOUNDED_ITERATORS
+ using __type _LIBCPP_NODEBUG = std::__bounded_iter<__pointer>;
+ using __const_type _LIBCPP_NODEBUG = std::__bounded_iter<__const_pointer>;
# else
- using iterator = __capacity_aware_iterator<__pointer, 1>;
- using const_iterator = __capacity_aware_iterator<__const_pointer, 1>;
+ using __type _LIBCPP_NODEBUG = std::__capacity_aware_iterator<__pointer, 1>;
+ using __const_type _LIBCPP_NODEBUG = std::__capacity_aware_iterator<__const_pointer, 1>;
# endif
+ template <class _PtrType>
+ static auto __make(_PtrType __p, [[__maybe_unused__]] unsigned __end_offset) noexcept {
+# if _LIBCPP_ABI_BOUNDED_ITERATORS
+ return std::__make_bounded_iter<_PtrType>(__p, __p, __p + __end_offset)
+# else
+ return std::__make_capacity_aware_iterator<_PtrType, 1>(__p);
+# endif
+ }
+ };
+
+ using __iter _LIBCPP_NODEBUG = __iterator<>;
+
+public:
+ using iterator = __iter::__type;
+ using const_iterator = __iter::__const_type;
+
// [optional.iterators], iterator support
- [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator begin() noexcept {
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI iterator begin() noexcept {
auto* __ptr = std::addressof(this->__val_);
-# ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
- return std::__make_bounded_iter(__ptr, __ptr, __ptr + (this->has_value() ? 1 : 0));
-# else
- return std::__make_capacity_aware_iterator<__pointer, 1>(__ptr);
-# endif
+ return __iter::__make(__ptr, this->has_value() ? 1 : 0);
}
- [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const_iterator begin() const noexcept {
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI const_iterator begin() const noexcept {
auto* __ptr = std::addressof(this->__val_);
-# ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
- return std::__make_bounded_iter(__ptr, __ptr, __ptr + (this->has_value() ? 1 : 0));
-# else
- return std::__make_capacity_aware_iterator<__const_pointer, 1>(__ptr);
-# endif
+ return __iter::__make(__ptr, this->has_value() ? 1 : 0);
}
- [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator end() noexcept {
- return begin() + (this->has_value() ? 1 : 0);
- }
- [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const_iterator end() const noexcept {
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI iterator end() noexcept { return begin() + (this->has_value() ? 1 : 0); }
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI const_iterator end() const noexcept {
return begin() + (this->has_value() ? 1 : 0);
}
-# endif // _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
+# endif // _LIBCPP_STD_VER >= 26
};
_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__optional/optional_ref.h b/libcxx/include/__optional/optional_ref.h
index 93ce0a9fa6746d..8b411475ae2522 100644
--- a/libcxx/include/__optional/optional_ref.h
+++ b/libcxx/include/__optional/optional_ref.h
@@ -16,6 +16,7 @@
#include <__fwd/optional.h>
#include <__iterator/bounded_iter.h>
#include <__iterator/capacity_aware_iterator.h>
+#include <__iterator/static_packed_bounded_iter.h>
#include <__memory/addressof.h>
#include <__optional/common.h>
#include <__optional/comparison.h>
@@ -55,19 +56,41 @@ _LIBCPP_BEGIN_NAMESPACE_STD
template <class _Tp>
struct __optional_ref_iterator_base {};
-# if _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
-
template <class _Tp>
requires(is_object_v<_Tp> && !__is_unbounded_array_v<_Tp>)
struct __optional_ref_iterator_base<_Tp&> {
-# ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
- using iterator = __bounded_iter<_Tp*>;
-# else
- using iterator = __capacity_aware_iterator<_Tp*, 1>;
-# endif
-};
+protected:
+ using __pointer = add_pointer_t<_Tp>;
+
+ template <bool = std::__range_fits_in_alignment(alignof(_Tp), 1)>
+ struct _LIBCPP_NODEBUG __iterator {
+ using __type = std::__static_packed_bounded_iterator<__pointer, 1>;
+
+ static __type __make(__pointer __p, unsigned) noexcept {
+ return std::__make_static_packed_bounded_iter<__pointer, 1>(__p, 0);
+ }
+ };
+
+ template <>
+ struct _LIBCPP_NODEBUG __iterator<false> {
+# if _LIBCPP_ABI_BOUNDED_ITERATORS
+ using __type = std::__bounded_iter<__pointer>;
+# else
+ using __type = std::__capacity_aware_iterator<__pointer, 1>;
+# endif
+ static __type __make(__pointer __p, [[__maybe_unused__]] unsigned __end_offset) noexcept {
+# if _LIBCPP_ABI_BOUNDED_ITERATORS
+ return std::__make_bounded_iter<__pointer>(__p, __p, __p + __end_offset);
+# else
+ return std::__make_capacity_aware_iterator<__pointer, 1>(__p);
# endif
+ }
+ };
+
+public:
+ using iterator = __iterator<>::__type;
+};
template <class _Tp>
class optional<_Tp&> : public __optional_ref_iterator_base<_Tp&> {
@@ -275,24 +298,18 @@ class optional<_Tp&> : public __optional_ref_iterator_base<_Tp&> {
_LIBCPP_HIDE_FROM_ABI constexpr void reset() noexcept { __value_ = nullptr; }
// [optional.ref.iterators], iterator support
-# if _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
- [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const noexcept
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI auto begin() const noexcept
requires __has_iterator_
{
-# ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
- return std::__make_bounded_iter(__value_, __value_, __value_ + (this->has_value() ? 1 : 0));
-# else
- return std::__make_capacity_aware_iterator<_Tp*, 1>(__value_);
-# endif
+ return __optional_ref_iterator_base<_Tp&>::template __iterator<>::__make(__value_, this->has_value() ? 1 : 0);
}
- [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const noexcept
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI auto end() const noexcept
requires __has_iterator_
{
return begin() + (this->has_value() ? 1 : 0);
}
-# endif
};
_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/test/libcxx/utilities/optional/optional.iterator/assert.arithmetic.pass.cpp b/libcxx/test/libcxx/utilities/optional/optional.iterator/assert.arithmetic.pass.cpp
index fe348a12962bad..7f7e593cfc6a6a 100644
--- a/libcxx/test/libcxx/utilities/optional/optional.iterator/assert.arithmetic.pass.cpp
+++ b/libcxx/test/libcxx/utilities/optional/optional.iterator/assert.arithmetic.pass.cpp
@@ -18,9 +18,82 @@
#include "check_assertion.h"
-int main(int, char**) {
+// If T fits the requirements for __static_packed_bounded_iterator, optional<T>'s iterator will always be that,
+// regardless of if the ABI switch is on or off. This is because it has less overhead than bounded_iter while providing the same guarantees for the on case,
+// while for the off case, it has more guarantees than __capacity_aware_iterator while occupying the same amount of space.
+// Otherwise, optional<T>'s iterator is __capacity_aware_iterator.
+void test_packed() {
+ // int has enough room, 3 bits free -> 0-2 range
+ { // operator++
+ std::optional<int> o{1};
+ auto i = o.end();
+
+ TEST_LIBCPP_ASSERT_FAILURE(
+ ++i, "__static_packed_bounded_iterator::operator++: Attempt to advance an iterator past the end");
+ TEST_LIBCPP_ASSERT_FAILURE(
+ i++, "__static_packed_bounded_iterator::operator++: Attempt to advance an iterator past the end");
+ }
+
+ { // operator--
+ std::optional<int> o{1};
+ auto i = o.begin();
+
+ TEST_LIBCPP_ASSERT_FAILURE(
+ --i, "__static_packed_bounded_iterator::operator--: Attempt to rewind an iterator past the start");
+ TEST_LIBCPP_ASSERT_FAILURE(
+ i--, "__static_packed_bounded_iterator::operator--: Attempt to rewind an iterator past the start");
+ }
+
+ { // operator*
+ std::optional<int> o;
+ auto i = o.begin();
+
+ TEST_LIBCPP_ASSERT_FAILURE(
+ *i, "__static_packed_bounded_iterator::operator*: Attempt to dereference an iterator at the end");
+ }
+
+ { // operator[]
+ std::optional<int> o{1};
+ auto i = o.begin();
+
+ TEST_LIBCPP_ASSERT_FAILURE(
+ i[1], "__static_packed_bounded_iterator::operator[]: Attempt to index an iterator at or past the end");
+ TEST_LIBCPP_ASSERT_FAILURE(
+ i[-1], "__static_packed_bounded_iterator::operator[]: Attempt to index an iterator past the start");
+ }
+
+ { // operator->
+ std::optional<int> o{1};
+ auto i = o.end();
+
+ TEST_LIBCPP_ASSERT_FAILURE(
+ i.operator->(), "__static_packed_bounded_iterator::operator->: Attempt to dereference an iterator at the end");
+ }
+
+ { // operator+=
+ std::optional<int> o{1};
+ auto i = o.begin();
+
+ TEST_LIBCPP_ASSERT_FAILURE(
+ i += 2, "__static_packed_bounded_iterator::operator+=: Attempt to advance an iterator past the end");
+ TEST_LIBCPP_ASSERT_FAILURE(
+ i += -1, "__static_packed_bounded_iterator::operator+=: Attempt to rewind an iterator past the start");
+ }
+
+ { // operator-=
+ std::optional<int> o{1};
+ auto i = o.begin();
+
+ TEST_LIBCPP_ASSERT_FAILURE(
+ i -= 1, "__static_packed_bounded_iterator::operator-=: Attempt to rewind an iterator past the start");
+ TEST_LIBCPP_ASSERT_FAILURE(
+ i -= -2, "__static_packed_bounded_iterator::operator-=: Attempt to advance an iterator past the end");
+ }
+}
+
+void test_nonpacked() {
{
- std::optional<int> opt(1);
+ std::optional<short> opt(1);
auto i = opt.begin();
TEST_LIBCPP_ASSERT_FAILURE(
@@ -47,6 +120,10 @@ int main(int, char**) {
i[-2],
"__capacity_aware_iterator::operator[]: Attempting to index iterator past its container's possible range");
}
+}
+int main(int, char**) {
+ test_packed();
+ test_nonpacked();
return 0;
}
diff --git a/libcxx/test/libcxx/utilities/optional/optional.object/optional.iterator/assert.bounded_iterator.pass.cpp b/libcxx/test/libcxx/utilities/optional/optional.object/optional.iterator/assert.bounded_iterator.pass.cpp
index a7aecabd75a012..c3eba8f6f8181c 100644
--- a/libcxx/test/libcxx/utilities/optional/optional.object/optional.iterator/assert.bounded_iterator.pass.cpp
+++ b/libcxx/test/libcxx/utilities/optional/optional.object/optional.iterator/assert.bounded_iterator.pass.cpp
@@ -27,17 +27,93 @@
#include "check_assertion.h"
-int main(int, char**) {
+// if T has the alignment required to fit a bounds check, optional<T>
+// will always have static_packed_bounded_iter regardless of if the bounded iterator
+// option is enabled for it.
+// see __static_packed_bounded_iterator.h for more information on how this is determined.
+
+void test_packed_iter() {
+ // int has enough room, 3 bits free -> 0-2 range
{ // operator++
std::optional<int> o{1};
auto i = o.end();
+ TEST_LIBCPP_ASSERT_FAILURE(
+ ++i, "__static_packed_bounded_iterator::operator++: Attempt to advance an iterator past the end");
+ TEST_LIBCPP_ASSERT_FAILURE(
+ i++, "__static_packed_bounded_iterator::operator++: Attempt to advance an iterator past the end");
+ }
+
+ { // operator--
+ std::optional<int> o{1};
+ auto i = o.begin();
+
+ TEST_LIBCPP_ASSERT_FAILURE(
+ --i, "__static_packed_bounded_iterator::operator--: Attempt to rewind an iterator past the start");
+ TEST_LIBCPP_ASSERT_FAILURE(
+ i--, "__static_packed_bounded_iterator::operator--: Attempt to rewind an iterator past the start");
+ }
+
+ { // operator*
+ std::optional<int> o;
+ auto i = o.begin();
+
+ TEST_LIBCPP_ASSERT_FAILURE(
+ *i, "__static_packed_bounded_iterator::operator*: Attempt to dereference an iterator at the end");
+ }
+
+ { // operator[]
+ std::optional<int> o{1};
+ auto i = o.begin();
+
+ TEST_LIBCPP_ASSERT_FAILURE(
+ i[1], "__static_packed_bounded_iterator::operator[]: Attempt to index an iterator at or past the end");
+ TEST_LIBCPP_ASSERT_FAILURE(
+ i[-1], "__static_packed_bounded_iterator::operator[]: Attempt to index an iterator past the start");
+ }
+
+ { // operator->
+ std::optional<int> o{1};
+ auto i = o.end();
+
+ TEST_LIBCPP_ASSERT_FAILURE(
+ i.operator->(), "__static_packed_bounded_iterator::operator->: Attempt to dereference an iterator at the end");
+ }
+
+ { // operator+=
+ std::optional<int> o{1};
+ auto i = o.begin();
+
+ TEST_LIBCPP_ASSERT_FAILURE(
+ i += 2, "__static_packed_bounded_iterator::operator+=: Attempt to advance an iterator past the end");
+ TEST_LIBCPP_ASSERT_FAILURE(
+ i += -1, "__static_packed_bounded_iterator::operator+=: Attempt to rewind an iterator past the start");
+ }
+
+ { // operator-=
+ std::optional<int> o{1};
+ auto i = o.begin();
+
+ TEST_LIBCPP_ASSERT_FAILURE(
+ i -= 1, "__static_packed_bounded_iterator::operator-=: Attempt to rewind an iterator past the start");
+ TEST_LIBCPP_ASSERT_FAILURE(
+ i -= -2, "__static_packed_bounded_iterator::operator-=: Attempt to advance an iterator past the end");
+ }
+}
+
+// short doesn't have the alignment required to use static_packed_bounded_iter
+// so optional<short>'s iterator is bounded_iter with the option enabled.
+void test_bounded_iter() {
+ { // operator++
+ std::optional<short> o{1};
+ auto i = o.end();
+
TEST_LIBCPP_ASSERT_FAILURE(++i, "__bounded_iter::operator++: Attempt to advance an iterator past the end");
TEST_LIBCPP_ASSERT_FAILURE(i++, "__bounded_iter::operator++: Attempt to advance an iterator past the end");
}
{ // operator--
- std::optional<int> o{1};
+ std::optional<short> o{1};
auto i = o.begin();
TEST_LIBCPP_ASSERT_FAILURE(--i, "__bounded_iter::operator--: Attempt to rewind an iterator past the start");
@@ -45,14 +121,14 @@ int main(int, char**) {
}
{ // operator*
- std::optional<int> o;
+ std::optional<short> o;
auto i = o.begin();
TEST_LIBCPP_ASSERT_FAILURE(*i, "__bounded_iter::operator*: Attempt to dereference an iterator at the end");
}
{ // operator[]
- std::optional<int> o{1};
+ std::optional<short> o{1};
auto i = o.begin();
TEST_LIBCPP_ASSERT_FAILURE(i[1], "__bounded_iter::operator[]: Attempt to index an iterator at or past the end");
@@ -60,7 +136,7 @@ int main(int, char**) {
}
{ // operator->
- std::optional<int> o{1};
+ std::optional<short> o{1};
auto i = o.end();
TEST_LIBCPP_ASSERT_FAILURE(
@@ -68,7 +144,7 @@ int main(int, char**) {
}
{ // operator+=
- std::optional<int> o{1};
+ std::optional<short> o{1};
auto i = o.begin();
TEST_LIBCPP_ASSERT_FAILURE(i += 2, "__bounded_iter::operator+=: Attempt to advance an iterator past the end");
@@ -76,12 +152,17 @@ int main(int, char**) {
}
{ // operator-=
- std::optional<int> o{1};
+ std::optional<short> o{1};
auto i = o.begin();
TEST_LIBCPP_ASSERT_FAILURE(i -= 1, "__bounded_iter::operator-=: Attempt to rewind an iterator past the start");
TEST_LIBCPP_ASSERT_FAILURE(i -= -2, "__bounded_iter::operator-=: Attempt to advance an iterator past the end");
}
+}
+
+int main(int, char**) {
+ test_bounded_iter();
+ test_packed_iter();
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
index 81234525923a10..196227e9f7dcff 100644
--- a/libcxx/test/std/utilities/optional/optional.iterator/begin.pass.cpp
+++ b/libcxx/test/std/utilities/optional/optional.iterator/begin.pass.cpp
@@ -52,18 +52,23 @@ constexpr bool test() {
constexpr bool tests() {
assert(test<int>());
assert(test<char>());
+ assert(test<short>());
assert(test<const int>());
assert(test<const char>());
assert(test<int&>());
assert(test<char&>());
+ assert(test<short&>());
assert(test<const int&>());
assert(test<const char&>());
+ assert(test<const short&>());
return true;
}
int main(int, char**) {
assert(tests());
- static_assert(tests());
+
+ // blocked on constexpr pointer tagging
+ // static_assert(tests());
return 0;
}
diff --git a/libcxx/test/std/utilities/optional/optional.iterator/borrowed_range.compile.pass.cpp b/libcxx/test/std/utilities/optional/optional.iterator/borrowed_range.compile.pass.cpp
index a79d1d51a5b11c..91bd61b1ac8ce6 100644
--- a/libcxx/test/std/utilities/optional/optional.iterator/borrowed_range.compile.pass.cpp
+++ b/libcxx/test/std/utilities/optional/optional.iterator/borrowed_range.compile.pass.cpp
@@ -31,4 +31,6 @@ void test_borrowed_range() {
borrowed_range<int[]>();
borrowed_range<int[10]>();
borrowed_range<int()>();
+ borrowed_range<char>();
+ borrowed_range<short>();
}
diff --git a/libcxx/test/std/utilities/optional/optional.iterator/compare.pass.cpp b/libcxx/test/std/utilities/optional/optional.iterator/compare.pass.cpp
index b3f778328c0cc9..38c9f5fdea2853 100644
--- a/libcxx/test/std/utilities/optional/optional.iterator/compare.pass.cpp
+++ b/libcxx/test/std/utilities/optional/optional.iterator/compare.pass.cpp
@@ -93,7 +93,8 @@ constexpr bool test() {
int main(int, char**) {
test();
- static_assert(test());
+ // blocked on constexpr pointer tagging
+ // static_assert(test());
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
index c62c9fc7746d6a..22eb3892633827 100644
--- a/libcxx/test/std/utilities/optional/optional.iterator/end.pass.cpp
+++ b/libcxx/test/std/utilities/optional/optional.iterator/end.pass.cpp
@@ -62,19 +62,23 @@ constexpr bool test() {
constexpr bool tests() {
assert(test<int>());
assert(test<char>());
+ assert(test<short>());
assert(test<const int>());
assert(test<const char>());
assert(test<int&>());
assert(test<char&>());
+ assert(test<short&>());
assert(test<const int&>());
assert(test<const char&>());
-
+ assert(test<const short&>());
return true;
}
int main(int, char**) {
assert(tests());
- static_assert(tests());
+
+ // blocked on constexpr pointer tagging
+ // 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 996811d078b869..502cbfdb4b85a0 100644
--- a/libcxx/test/std/utilities/optional/optional.iterator/iterator.pass.cpp
+++ b/libcxx/test/std/utilities/optional/optional.iterator/iterator.pass.cpp
@@ -139,13 +139,16 @@ constexpr bool test() {
test<int>(1);
test<char>('a');
test<bool>(true);
+ test<short>(2);
test<const int>(2);
test<const char>('b');
test<int&>(1);
test<char&>('a');
test<bool&>(true);
+ test<short&>(1);
test<const int&>(2);
test<const char&>('b');
+ test<const short&>(1);
static_assert(!std::ranges::range<std::optional<int (&)()>>);
static_assert(!std::ranges::range<std::optional<int (&)[]>>);
@@ -156,7 +159,8 @@ constexpr bool test() {
int main(int, char**) {
assert(test());
- static_assert(test());
+
+ // static_assert(test());
return 0;
}
>From a180b94cf9737e38c6c69ba256cf4bb6b3e54cf9 Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Fri, 2 Oct 2026 14:48:02 -0400
Subject: [PATCH 4/8] Fixup
---
libcxx/include/__optional/optional.h | 8 ++++----
libcxx/include/__optional/optional_ref.h | 8 ++++----
.../optional.version.compile.pass.cpp | 16 +++++-----------
.../version.version.compile.pass.cpp | 16 +++++-----------
4 files changed, 18 insertions(+), 30 deletions(-)
diff --git a/libcxx/include/__optional/optional.h b/libcxx/include/__optional/optional.h
index bbd53528ccdd5c..98ba4ea8761e9d 100644
--- a/libcxx/include/__optional/optional.h
+++ b/libcxx/include/__optional/optional.h
@@ -689,7 +689,7 @@ class _LIBCPP_DECLSPEC_EMPTY_BASES optional
using __const_pointer _LIBCPP_NODEBUG = add_pointer_t<const _Tp>;
template <bool = std::__range_fits_in_alignment(alignof(_Tp), 1)>
- struct _LIBCPP_NODEBUG __iterator {
+ struct __iterator {
using __type _LIBCPP_NODEBUG = std::__static_packed_bounded_iterator<__pointer, 1>;
using __const_type _LIBCPP_NODEBUG = std::__static_packed_bounded_iterator<__const_pointer, 1>;
@@ -700,8 +700,8 @@ class _LIBCPP_DECLSPEC_EMPTY_BASES optional
};
template <>
- struct _LIBCPP_NODEBUG __iterator<false> {
-# if _LIBCPP_ABI_BOUNDED_ITERATORS
+ struct __iterator<false> {
+# ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
using __type _LIBCPP_NODEBUG = std::__bounded_iter<__pointer>;
using __const_type _LIBCPP_NODEBUG = std::__bounded_iter<__const_pointer>;
# else
@@ -711,7 +711,7 @@ class _LIBCPP_DECLSPEC_EMPTY_BASES optional
template <class _PtrType>
static auto __make(_PtrType __p, [[__maybe_unused__]] unsigned __end_offset) noexcept {
-# if _LIBCPP_ABI_BOUNDED_ITERATORS
+# ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
return std::__make_bounded_iter<_PtrType>(__p, __p, __p + __end_offset)
# else
return std::__make_capacity_aware_iterator<_PtrType, 1>(__p);
diff --git a/libcxx/include/__optional/optional_ref.h b/libcxx/include/__optional/optional_ref.h
index 8b411475ae2522..fe0b5c81779179 100644
--- a/libcxx/include/__optional/optional_ref.h
+++ b/libcxx/include/__optional/optional_ref.h
@@ -63,7 +63,7 @@ struct __optional_ref_iterator_base<_Tp&> {
using __pointer = add_pointer_t<_Tp>;
template <bool = std::__range_fits_in_alignment(alignof(_Tp), 1)>
- struct _LIBCPP_NODEBUG __iterator {
+ struct __iterator {
using __type = std::__static_packed_bounded_iterator<__pointer, 1>;
static __type __make(__pointer __p, unsigned) noexcept {
@@ -72,15 +72,15 @@ struct __optional_ref_iterator_base<_Tp&> {
};
template <>
- struct _LIBCPP_NODEBUG __iterator<false> {
-# if _LIBCPP_ABI_BOUNDED_ITERATORS
+ struct __iterator<false> {
+# ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
using __type = std::__bounded_iter<__pointer>;
# else
using __type = std::__capacity_aware_iterator<__pointer, 1>;
# endif
static __type __make(__pointer __p, [[__maybe_unused__]] unsigned __end_offset) noexcept {
-# if _LIBCPP_ABI_BOUNDED_ITERATORS
+# ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
return std::__make_bounded_iter<__pointer>(__p, __p, __p + __end_offset);
# else
return std::__make_capacity_aware_iterator<__pointer, 1>(__p);
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 ae403fadcc2335..7ba0bdaef5477f 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
@@ -182,17 +182,11 @@
# error "__cpp_lib_optional should have the value 202506L in c++29"
# endif
-# if !defined(_LIBCPP_VERSION) || _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
-# ifndef __cpp_lib_optional_range_support
-# error "__cpp_lib_optional_range_support should be defined in c++29"
-# endif
-# if __cpp_lib_optional_range_support != 202406L
-# error "__cpp_lib_optional_range_support should have the value 202406L in c++29"
-# endif
-# else
-# ifdef __cpp_lib_optional_range_support
-# error "__cpp_lib_optional_range_support should not be defined when the requirement '!defined(_LIBCPP_VERSION) || _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR' is not met!"
-# endif
+# ifndef __cpp_lib_optional_range_support
+# error "__cpp_lib_optional_range_support should be defined in c++29"
+# endif
+# if __cpp_lib_optional_range_support != 202406L
+# error "__cpp_lib_optional_range_support should have the value 202406L in c++29"
# endif
#endif // TEST_STD_VER > 26
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 e85af92365f7bc..2b3c27a2c69ef1 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
@@ -9978,17 +9978,11 @@
# error "__cpp_lib_optional should have the value 202506L in c++29"
# endif
-# if !defined(_LIBCPP_VERSION) || _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
-# ifndef __cpp_lib_optional_range_support
-# error "__cpp_lib_optional_range_support should be defined in c++29"
-# endif
-# if __cpp_lib_optional_range_support != 202406L
-# error "__cpp_lib_optional_range_support should have the value 202406L in c++29"
-# endif
-# else
-# ifdef __cpp_lib_optional_range_support
-# error "__cpp_lib_optional_range_support should not be defined when the requirement '!defined(_LIBCPP_VERSION) || _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR' is not met!"
-# endif
+# ifndef __cpp_lib_optional_range_support
+# error "__cpp_lib_optional_range_support should be defined in c++29"
+# endif
+# if __cpp_lib_optional_range_support != 202406L
+# error "__cpp_lib_optional_range_support should have the value 202406L in c++29"
# endif
# ifndef __cpp_lib_out_ptr
>From 21f3f553b07c2df06c496857a6e488c8a6a8b967 Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Fri, 2 Oct 2026 15:20:57 -0400
Subject: [PATCH 5/8] annotate internal types as nodebug
---
libcxx/include/__optional/optional_ref.h | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/libcxx/include/__optional/optional_ref.h b/libcxx/include/__optional/optional_ref.h
index fe0b5c81779179..939a2c0e361555 100644
--- a/libcxx/include/__optional/optional_ref.h
+++ b/libcxx/include/__optional/optional_ref.h
@@ -60,11 +60,11 @@ template <class _Tp>
requires(is_object_v<_Tp> && !__is_unbounded_array_v<_Tp>)
struct __optional_ref_iterator_base<_Tp&> {
protected:
- using __pointer = add_pointer_t<_Tp>;
+ using __pointer _LIBCPP_NODEBUG = add_pointer_t<_Tp>;
template <bool = std::__range_fits_in_alignment(alignof(_Tp), 1)>
struct __iterator {
- using __type = std::__static_packed_bounded_iterator<__pointer, 1>;
+ using __type _LIBCPP_NODEBUG = std::__static_packed_bounded_iterator<__pointer, 1>;
static __type __make(__pointer __p, unsigned) noexcept {
return std::__make_static_packed_bounded_iter<__pointer, 1>(__p, 0);
@@ -74,9 +74,9 @@ struct __optional_ref_iterator_base<_Tp&> {
template <>
struct __iterator<false> {
# ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
- using __type = std::__bounded_iter<__pointer>;
+ using __type _LIBCPP_NO_DEBUG = std::__bounded_iter<__pointer>;
# else
- using __type = std::__capacity_aware_iterator<__pointer, 1>;
+ using __type _LIBCPP_NODEBUG = std::__capacity_aware_iterator<__pointer, 1>;
# endif
static __type __make(__pointer __p, [[__maybe_unused__]] unsigned __end_offset) noexcept {
>From 7b31bb35fe310c32b5b105ad20f36a2d4e4081cc Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Fri, 2 Oct 2026 15:52:16 -0400
Subject: [PATCH 6/8] simplify a little
---
libcxx/include/__optional/optional.h | 6 +--
libcxx/include/__optional/optional_ref.h | 48 ++++++++++--------------
2 files changed, 23 insertions(+), 31 deletions(-)
diff --git a/libcxx/include/__optional/optional.h b/libcxx/include/__optional/optional.h
index 98ba4ea8761e9d..f1c5f2b8d3f356 100644
--- a/libcxx/include/__optional/optional.h
+++ b/libcxx/include/__optional/optional.h
@@ -688,7 +688,7 @@ class _LIBCPP_DECLSPEC_EMPTY_BASES optional
using __pointer _LIBCPP_NODEBUG = add_pointer_t<_Tp>;
using __const_pointer _LIBCPP_NODEBUG = add_pointer_t<const _Tp>;
- template <bool = std::__range_fits_in_alignment(alignof(_Tp), 1)>
+ template <class _Up = _Tp, bool = std::__range_fits_in_alignment(alignof(_Up), 1)>
struct __iterator {
using __type _LIBCPP_NODEBUG = std::__static_packed_bounded_iterator<__pointer, 1>;
using __const_type _LIBCPP_NODEBUG = std::__static_packed_bounded_iterator<__const_pointer, 1>;
@@ -699,8 +699,8 @@ class _LIBCPP_DECLSPEC_EMPTY_BASES optional
}
};
- template <>
- struct __iterator<false> {
+ template <class _Up>
+ struct __iterator<_Up, false> {
# ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
using __type _LIBCPP_NODEBUG = std::__bounded_iter<__pointer>;
using __const_type _LIBCPP_NODEBUG = std::__bounded_iter<__const_pointer>;
diff --git a/libcxx/include/__optional/optional_ref.h b/libcxx/include/__optional/optional_ref.h
index 939a2c0e361555..52aa031d743bd7 100644
--- a/libcxx/include/__optional/optional_ref.h
+++ b/libcxx/include/__optional/optional_ref.h
@@ -24,6 +24,7 @@
#include <__optional/nullopt_t.h>
#include <__optional/swap.h>
#include <__type_traits/add_pointer.h>
+#include <__type_traits/conditional.h>
#include <__type_traits/decay.h>
#include <__type_traits/invoke.h>
#include <__type_traits/is_array.h>
@@ -59,37 +60,19 @@ struct __optional_ref_iterator_base {};
template <class _Tp>
requires(is_object_v<_Tp> && !__is_unbounded_array_v<_Tp>)
struct __optional_ref_iterator_base<_Tp&> {
-protected:
+private:
using __pointer _LIBCPP_NODEBUG = add_pointer_t<_Tp>;
- template <bool = std::__range_fits_in_alignment(alignof(_Tp), 1)>
- struct __iterator {
- using __type _LIBCPP_NODEBUG = std::__static_packed_bounded_iterator<__pointer, 1>;
-
- static __type __make(__pointer __p, unsigned) noexcept {
- return std::__make_static_packed_bounded_iter<__pointer, 1>(__p, 0);
- }
- };
-
- template <>
- struct __iterator<false> {
-# ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
- using __type _LIBCPP_NO_DEBUG = std::__bounded_iter<__pointer>;
-# else
- using __type _LIBCPP_NODEBUG = std::__capacity_aware_iterator<__pointer, 1>;
-# endif
-
- static __type __make(__pointer __p, [[__maybe_unused__]] unsigned __end_offset) noexcept {
-# ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
- return std::__make_bounded_iter<__pointer>(__p, __p, __p + __end_offset);
+public:
+ using iterator =
+ conditional_t< std::__range_fits_in_alignment(alignof(_Tp), 1),
+ std::__static_packed_bounded_iterator<__pointer, 1>,
+# if _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
+ std::__bounded_iter<__pointer>
# else
- return std::__make_capacity_aware_iterator<__pointer, 1>(__p);
+ std::__capacity_aware_iterator<__pointer, 1>
# endif
- }
- };
-
-public:
- using iterator = __iterator<>::__type;
+ >;
};
template <class _Tp>
@@ -302,7 +285,16 @@ class optional<_Tp&> : public __optional_ref_iterator_base<_Tp&> {
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI auto begin() const noexcept
requires __has_iterator_
{
- return __optional_ref_iterator_base<_Tp&>::template __iterator<>::__make(__value_, this->has_value() ? 1 : 0);
+ using __pointer _LIBCPP_NODEBUG = add_pointer_t<_Tp>;
+ if constexpr (std::__range_fits_in_alignment(alignof(_Tp&), 1)) {
+ return std::__make_static_packed_bounded_iter<__pointer, 1>(__value_, 0);
+ } else {
+# ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
+ return std::__make_bounded_iter<__pointer>(__value_, __value_, __value_ + this->has_value() ? 1 : 0);
+# else
+ return std::__make_capacity_aware_iterator<__pointer, 1>(__value_);
+# endif
+ }
}
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI auto end() const noexcept
>From acefa847dd30aa301867ceed8ab9dd8415a7de98 Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Fri, 2 Oct 2026 21:24:41 -0400
Subject: [PATCH 7/8] Fixup
---
libcxx/include/__optional/optional_ref.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libcxx/include/__optional/optional_ref.h b/libcxx/include/__optional/optional_ref.h
index 52aa031d743bd7..d95684012b5086 100644
--- a/libcxx/include/__optional/optional_ref.h
+++ b/libcxx/include/__optional/optional_ref.h
@@ -67,7 +67,7 @@ struct __optional_ref_iterator_base<_Tp&> {
using iterator =
conditional_t< std::__range_fits_in_alignment(alignof(_Tp), 1),
std::__static_packed_bounded_iterator<__pointer, 1>,
-# if _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
+# ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
std::__bounded_iter<__pointer>
# else
std::__capacity_aware_iterator<__pointer, 1>
>From cb8a459eb03fc6d19622abfeb41b5c4f2d2f0ee6 Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Fri, 2 Oct 2026 21:26:10 -0400
Subject: [PATCH 8/8] Update hardening docs
---
libcxx/docs/Hardening.md | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/libcxx/docs/Hardening.md b/libcxx/docs/Hardening.md
index 38d59775885da6..61fdabb1e90fd1 100644
--- a/libcxx/docs/Hardening.md
+++ b/libcxx/docs/Hardening.md
@@ -427,11 +427,12 @@ CMake configuration time. The available options are:
ABI impact: changes the iterator type of `std::array`, its size and its layout.
-- `_LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL` -- changes the iterator type of `std::optional` to a
- bounded iterator that keeps track of whether it's within the bounds of its container and asserts it
- on every dereference and when performing iterator arithmetic.
+- `_LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL` -- For `T` with alignment <= 2, this changes the iterator type
+ of `std::optional` to a bounded iterator that keeps track of whether it's within the bounds of its container
+ and asserts it on every dereference and when performing iterator arithmetic. For alignment > 2, this option has
+ no impact, as the default iterator type has bounds checking, but without any memory overhead.
- ABI impact: changes the iterator type of `std::optional`.
+ ABI impact: changes the iterator type of `std::optional` for `T` with alignment <= 2.
### ABI tags
More information about the libcxx-commits
mailing list