[libcxx-commits] [libcxx] [libc++] Implement a type-safe iterator for optional (PR #154239)
William Tran-Viet via libcxx-commits
libcxx-commits at lists.llvm.org
Fri Dec 5 21:46:45 PST 2025
https://github.com/smallp-o-p updated https://github.com/llvm/llvm-project/pull/154239
>From 6a1545ff95a398855039250302df6d6c2a9f901f Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Mon, 18 Aug 2025 20:09:40 -0400
Subject: [PATCH 1/9] Implement a type-safe iterator for optional
---
libcxx/include/CMakeLists.txt | 1 +
.../__iterator/upper_bounded_iterator.h | 174 ++++++++++++++++++
libcxx/include/module.modulemap.in | 1 +
libcxx/include/optional | 33 ++--
.../iterator.compile.pass.cpp | 28 ++-
5 files changed, 220 insertions(+), 17 deletions(-)
create mode 100644 libcxx/include/__iterator/upper_bounded_iterator.h
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 85a2f8ac8ec4b..587bb0dee1a08 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -514,6 +514,7 @@ set(files
__iterator/sortable.h
__iterator/static_bounded_iter.h
__iterator/unreachable_sentinel.h
+ __iterator/upper_bounded_iterator.h
__iterator/wrap_iter.h
__locale
__locale_dir/check_grouping.h
diff --git a/libcxx/include/__iterator/upper_bounded_iterator.h b/libcxx/include/__iterator/upper_bounded_iterator.h
new file mode 100644
index 0000000000000..1ae0d11ce379f
--- /dev/null
+++ b/libcxx/include/__iterator/upper_bounded_iterator.h
@@ -0,0 +1,174 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+/*
+ * __upper_bounded_iterator is an iterator that wraps an underlying iterator.
+ * It stores the underlying container type to prevent mixing iterators, and allow algorithms
+ * to optimize based on the underlying container type.
+ * It also stores the absolute maximum amount of elements the container can have, known at compile-time.
+ * As of writing, the only standard library containers which have this property are inplace_vector and optional.
+ */
+
+#ifndef _LIBCPP___ITERATOR_UPPER_BOUNDED_ITERATOR_H
+#define _LIBCPP___ITERATOR_UPPER_BOUNDED_ITERATOR_H
+
+#include <__compare/ordering.h>
+#include <__compare/three_way_comparable.h>
+#include <__config>
+#include <__cstddef/size_t.h>
+#include <__iterator/incrementable_traits.h>
+#include <__iterator/iterator_traits.h>
+#include <__memory/pointer_traits.h>
+#include <__type_traits/is_constructible.h>
+#include <__utility/move.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+#if _LIBCPP_STD_VER >= 26
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+template <class _Iter, class _Container, std::size_t _Max_Elements>
+class __upper_bounded_iterator {
+private:
+ _Iter __iter_;
+
+ friend _Container;
+
+public:
+ using iterator_category = iterator_traits<_Iter>::iterator_category;
+ using iterator_concept = _Iter::iterator_concept;
+ using value_type = iter_value_t<_Iter>;
+ using difference_type = iter_difference_t<_Iter>;
+ using reference = iter_reference_t<_Iter>;
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __upper_bounded_iterator()
+ requires is_default_constructible_v<_Iter>
+ = default;
+
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __upper_bounded_iterator(_Iter __iter) : __iter_(std::move(__iter)) {}
+
+ _LIBCPP_HIDE_FROM_ABI constexpr _Iter __base() const noexcept(noexcept(_Iter(__iter_))) { return __iter_; }
+ _LIBCPP_HIDE_FROM_ABI constexpr auto __max_elements() const noexcept { return _Max_Elements; }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const { return *__iter_; }
+ _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator->() const
+ requires requires { __iter_.operator->(); }
+ {
+ return __iter_.operator->();
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __upper_bounded_iterator& operator++() {
+ ++__iter_;
+ return *this;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __upper_bounded_iterator operator++(int) {
+ __upper_bounded_iterator __tmp(*this);
+ ++*this;
+ return __tmp;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __upper_bounded_iterator& operator--() {
+ --__iter_;
+ return *this;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __upper_bounded_iterator operator--(int) {
+ __upper_bounded_iterator __tmp(*this);
+ --*this;
+ return __tmp;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __upper_bounded_iterator& operator+=(difference_type __x) {
+ __iter_ += __x;
+ return *this;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __upper_bounded_iterator& operator-=(difference_type __x) {
+ __iter_ -= __x;
+ return *this;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator[](difference_type __n) const { return *(*this + __n); }
+
+ _LIBCPP_HIDE_FROM_ABI friend constexpr bool
+ operator==(const __upper_bounded_iterator& __x, const __upper_bounded_iterator& __y) {
+ return __x.__iter_ == __y.__iter_;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI friend constexpr auto
+ operator<=>(const __upper_bounded_iterator& __x, const __upper_bounded_iterator& __y) {
+ if constexpr (three_way_comparable_with<_Iter, _Iter, strong_ordering>) {
+ return __x.__iter_ <=> __y.__iter_;
+ } else {
+ if (__x.__iter_ < __x.__iter_) {
+ return strong_ordering::less;
+ } else if (__x.__iter_ == __y.__iter_) {
+ return strong_ordering::equal;
+ }
+ return strong_ordering::greater;
+ }
+ }
+
+ template <class _Iter2>
+ _LIBCPP_HIDE_FROM_ABI friend constexpr auto
+ operator<=>(const __upper_bounded_iterator& __x,
+ const __upper_bounded_iterator<_Iter2, _Container, _Max_Elements> __y) {
+ if constexpr (three_way_comparable_with<_Iter, _Iter2, strong_ordering>) {
+ return __x.__iter_ <=> __y.__iter_;
+ } else {
+ if (__x.__iter_ < __x.__iter_) {
+ return strong_ordering::less;
+ } else if (__x.__iter_ == __y.__iter_) {
+ return strong_ordering::equal;
+ }
+ return strong_ordering::greater;
+ }
+ }
+
+ _LIBCPP_HIDE_FROM_ABI friend constexpr __upper_bounded_iterator
+ operator+(const __upper_bounded_iterator& __i, difference_type __n) {
+ auto __tmp = __i;
+ __tmp += __n;
+ return __tmp;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI friend constexpr __upper_bounded_iterator
+ operator+(difference_type __n, const __upper_bounded_iterator& __i) {
+ auto __tmp = __i;
+ __tmp += __n;
+ return __tmp;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI friend constexpr __upper_bounded_iterator
+ operator-(const __upper_bounded_iterator& __i, difference_type __n) {
+ return __i.__iter_ + __n;
+ }
+
+ template <class _Iter2>
+ _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+ operator-(const __upper_bounded_iterator& __x,
+ const __upper_bounded_iterator<_Iter2, _Container, _Max_Elements>& __y) {
+ return difference_type(__x.__base() - __y.__base());
+ }
+};
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STD_VER >= 26
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___ITERATOR_UPPER_BOUNDED_ITERATOR_H
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 5d2a92bf7905c..4b3669ec703ef 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1543,6 +1543,7 @@ module std [system] {
module sortable { header "__iterator/sortable.h" }
module static_bounded_iter { header "__iterator/static_bounded_iter.h" }
module unreachable_sentinel { header "__iterator/unreachable_sentinel.h" }
+ module upper_bounded_iterator { header "__iterator/upper_bounded_iterator.h" }
module wrap_iter { header "__iterator/wrap_iter.h" }
header "iterator"
diff --git a/libcxx/include/optional b/libcxx/include/optional
index 7b979d3d6d577..911ab52dccdac 100644
--- a/libcxx/include/optional
+++ b/libcxx/include/optional
@@ -264,7 +264,6 @@ namespace std {
# include <__compare/three_way_comparable.h>
# include <__concepts/invocable.h>
# include <__config>
-# include <__cstddef/ptrdiff_t.h>
# include <__exception/exception.h>
# include <__format/range_format.h>
# include <__functional/hash.h>
@@ -272,6 +271,7 @@ namespace std {
# include <__functional/unary_function.h>
# include <__fwd/functional.h>
# include <__iterator/bounded_iter.h>
+# include <__iterator/upper_bounded_iterator.h>
# include <__iterator/wrap_iter.h>
# include <__memory/addressof.h>
# include <__memory/construct_at.h>
@@ -688,52 +688,53 @@ private:
using __pointer _LIBCPP_NODEBUG = add_pointer_t<remove_reference_t<_Tp>>;
using __const_pointer _LIBCPP_NODEBUG = add_pointer_t<const remove_reference_t<_Tp>>;
-public:
# if _LIBCPP_STD_VER >= 26
+ template <typename _Underlying>
+ using __iter _LIBCPP_NODEBUG = __upper_bounded_iterator<_Underlying, __optional_iterator, 1>;
+
+public:
# ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
- using iterator = __bounded_iter<__wrap_iter<__pointer>>;
- using const_iterator = __bounded_iter<__wrap_iter<__const_pointer>>;
+ using iterator = __iter<__bounded_iter<__wrap_iter<__pointer>>>;
+ using const_iterator = __iter<__bounded_iter<__wrap_iter<__const_pointer>>>;
# else
- using iterator = __wrap_iter<__pointer>;
- using const_iterator = __wrap_iter<__const_pointer>;
+ using iterator = __iter<__wrap_iter<__pointer>>;
+ using const_iterator = __iter<__wrap_iter<__const_pointer>>;
# endif
// [optional.iterators], iterator support
_LIBCPP_HIDE_FROM_ABI constexpr iterator begin() noexcept {
auto& __derived_self = static_cast<optional<_Tp>&>(*this);
- auto __ptr = [&__derived_self]() {
+ auto __ptr = [&__derived_self] {
if constexpr (is_lvalue_reference_v<_Tp>) {
return __derived_self.has_value() ? std::addressof(__derived_self.__get()) : nullptr;
}
return std::addressof(__derived_self.__get());
}();
-
# ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
- return std::__make_bounded_iter(
+ return iterator(std::__make_bounded_iter(
__wrap_iter<__pointer>(__ptr),
__wrap_iter<__pointer>(__ptr),
- __wrap_iter<__pointer>(__ptr) + (__derived_self.has_value() ? 1 : 0));
+ __wrap_iter<__pointer>(__ptr) + (__derived_self.has_value() ? 1 : 0)));
# else
- return iterator(__ptr);
+ return iterator(__wrap_iter<__pointer>(__ptr));
# endif
}
_LIBCPP_HIDE_FROM_ABI constexpr const_iterator begin() const noexcept {
auto& __derived_self = static_cast<const optional<_Tp>&>(*this);
- auto* __ptr = [&__derived_self]() {
+ auto __ptr = [&__derived_self] {
if constexpr (is_lvalue_reference_v<_Tp>) {
return __derived_self.has_value() ? std::addressof(__derived_self.__get()) : nullptr;
}
return std::addressof(__derived_self.__get());
}();
-
# ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
- return std::__make_bounded_iter(
+ return const_iterator(std::__make_bounded_iter(
__wrap_iter<__const_pointer>(__ptr),
__wrap_iter<__const_pointer>(__ptr),
- __wrap_iter<__const_pointer>(__ptr) + (__derived_self.has_value() ? 1 : 0));
+ __wrap_iter<__const_pointer>(__ptr) + (__derived_self.has_value() ? 1 : 0)));
# else
- return const_iterator(__ptr);
+ return const_iterator(__wrap_iter<__const_pointer>(__ptr));
# endif
}
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 b604579e43557..424367b4f10b2 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
@@ -7,13 +7,14 @@
//===----------------------------------------------------------------------===//
// REQUIRES: std-at-least-c++26
-
+// UNSUPPORTED: generic-hardening
// <optional>
// template <class T> class optional::iterator;
// template <class T> class optional::const_iterator;
#include <optional>
+#include <type_traits>
template <typename T>
concept has_iterator_aliases = requires {
@@ -27,3 +28,28 @@ static_assert(has_iterator_aliases<std::optional<int&>>);
static_assert(has_iterator_aliases<std::optional<const int&>>);
static_assert(!has_iterator_aliases<std::optional<int (&)[1]>>);
static_assert(!has_iterator_aliases<std::optional<int (&)()>>);
+
+using Iter1 = std::optional<int>::iterator;
+using Iter2 = std::optional<double>::iterator;
+using Iter3 = std::optional<int>::const_iterator;
+using Iter4 = std::optional<double>::const_iterator;
+
+static_assert(std::is_convertible_v<Iter1, Iter1>);
+static_assert(!std::is_convertible_v<Iter1, Iter2>);
+static_assert(!std::is_convertible_v<Iter1, Iter3>);
+static_assert(!std::is_convertible_v<Iter1, Iter4>);
+
+static_assert(std::is_convertible_v<Iter2, Iter2>);
+static_assert(!std::is_convertible_v<Iter2, Iter1>);
+static_assert(!std::is_convertible_v<Iter2, Iter3>);
+static_assert(!std::is_convertible_v<Iter2, Iter4>);
+
+static_assert(std::is_convertible_v<Iter3, Iter3>);
+static_assert(!std::is_convertible_v<Iter3, Iter1>);
+static_assert(!std::is_convertible_v<Iter3, Iter2>);
+static_assert(!std::is_convertible_v<Iter3, Iter4>);
+
+static_assert(std::is_convertible_v<Iter4, Iter4>);
+static_assert(!std::is_convertible_v<Iter4, Iter1>);
+static_assert(!std::is_convertible_v<Iter4, Iter2>);
+static_assert(!std::is_convertible_v<Iter4, Iter3>);
>From d5927a7d9f2b909d36dfdfd57bc3ef6955b92e6b Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Mon, 18 Aug 2025 20:39:34 -0400
Subject: [PATCH 2/9] Update iterator tests
---
.../iterator.compile.pass.cpp | 2 +-
.../optional.iterator/iterator.pass.cpp | 24 +++++
.../iterator_compare.pass.cpp | 99 +++++++++++++++++++
3 files changed, 124 insertions(+), 1 deletion(-)
create mode 100644 libcxx/test/std/utilities/optional/optional.iterator/iterator_compare.pass.cpp
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 424367b4f10b2..442195bdcb948 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
@@ -7,7 +7,7 @@
//===----------------------------------------------------------------------===//
// REQUIRES: std-at-least-c++26
-// UNSUPPORTED: generic-hardening
+
// <optional>
// template <class T> class optional::iterator;
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 671fac35e732a..bb398b0fae4ac 100644
--- a/libcxx/test/std/utilities/optional/optional.iterator/iterator.pass.cpp
+++ b/libcxx/test/std/utilities/optional/optional.iterator/iterator.pass.cpp
@@ -86,6 +86,30 @@ constexpr bool test() {
assert(*(val.begin()) == v);
}
+ // [container.reqmts] operator-
+ {
+ std::optional<T> val(v);
+ auto it1 = val.begin();
+ auto it2 = val.begin();
+ auto it3 = val.end();
+
+ auto cit1 = std::as_const(val).begin();
+ auto cit2 = std::as_const(val).begin();
+ auto cit3 = std::as_const(val).end();
+
+ assert(it1 - it2 == 0);
+ assert(cit1 - cit2 == 0);
+ assert(it1 - cit1 == 0);
+ assert(it3 - it1 == 1);
+ assert(it1 - it3 == -1);
+
+ assert(cit3 - cit1 == 1);
+ assert(cit1 - cit3 == -1);
+ assert(cit3 - cit3 == 0);
+ assert(cit3 - it1 == 1);
+ assert(it1 - cit3 == -1);
+ }
+
return true;
}
diff --git a/libcxx/test/std/utilities/optional/optional.iterator/iterator_compare.pass.cpp b/libcxx/test/std/utilities/optional/optional.iterator/iterator_compare.pass.cpp
new file mode 100644
index 0000000000000..16b74ade9d82e
--- /dev/null
+++ b/libcxx/test/std/utilities/optional/optional.iterator/iterator_compare.pass.cpp
@@ -0,0 +1,99 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 <compare>
+#include <concepts>
+#include <optional>
+#include <type_traits>
+#include <utility>
+
+template<typename T>
+constexpr bool test() {
+ using Opt = std::optional<T>;
+ using I = Opt::iterator;
+ using CI = Opt::const_iterator;
+
+ static_assert(std::three_way_comparable<I>);
+ static_assert(std::three_way_comparable<CI>);
+
+ std::remove_reference_t<T> t{};
+ Opt opt{t};
+
+ // [container.reqmts] tests for comparison operators of optional::iterator and optional::const_iterator
+ auto it1 = opt.begin();
+
+ {
+ auto it2 = opt.begin();
+ assert(it1 == it2);
+ assert(!(it1 != it2));
+
+ static_assert(std::same_as<decltype(it1 <=> it2), std::strong_ordering>);
+ assert(it1 <=> it2 == std::strong_ordering::equal);
+ }
+
+ {
+ auto it3 = opt.end();
+ assert(it1 != it3);
+ assert(it1 <= it3);
+ assert(it1 < it3);
+ assert(it3 >= it1);
+ assert(it3 > it1);
+
+ assert(it1 <=> it3 == std::strong_ordering::less);
+ assert(it3 <=> it1 == std::strong_ordering::greater);
+ }
+
+ auto cit1 = std::as_const(opt).begin();
+
+ {
+ auto cit2 = std::as_const(opt).begin();
+ assert(cit1 == cit2);
+ assert(!(cit1 != cit2));
+
+ static_assert(std::same_as<decltype(cit1 <=> cit2), std::strong_ordering>);
+ assert(cit1 <=> cit2 == std::strong_ordering::equal);
+ }
+
+ {
+ auto cit3 = std::as_const(opt).end();
+
+ assert(cit1 <= cit3);
+ assert(cit1 < cit3);
+ assert(cit3 >= cit1);
+ assert(cit3 > cit1);
+
+ assert(cit1 <=> cit3 == std::strong_ordering::less);
+ assert(cit3 <=> cit1 == std::strong_ordering::greater);
+ }
+
+ return true;
+}
+
+
+constexpr bool test() {
+ test<int>();
+ test<char>();
+ test<int&>();
+
+ return true;
+}
+
+int main(int, char**) {
+ assert(test());
+ static_assert(test());
+
+ return 0;
+}
>From a5bb0c424168076c413e6ebc354d86ab2b379262 Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Tue, 2 Dec 2025 21:30:14 -0500
Subject: [PATCH 3/9] Formatting
---
.../optional/optional.iterator/iterator_compare.pass.cpp | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/libcxx/test/std/utilities/optional/optional.iterator/iterator_compare.pass.cpp b/libcxx/test/std/utilities/optional/optional.iterator/iterator_compare.pass.cpp
index 16b74ade9d82e..958519d4c805c 100644
--- a/libcxx/test/std/utilities/optional/optional.iterator/iterator_compare.pass.cpp
+++ b/libcxx/test/std/utilities/optional/optional.iterator/iterator_compare.pass.cpp
@@ -20,11 +20,11 @@
#include <type_traits>
#include <utility>
-template<typename T>
+template <typename T>
constexpr bool test() {
using Opt = std::optional<T>;
- using I = Opt::iterator;
- using CI = Opt::const_iterator;
+ using I = Opt::iterator;
+ using CI = Opt::const_iterator;
static_assert(std::three_way_comparable<I>);
static_assert(std::three_way_comparable<CI>);
@@ -82,7 +82,6 @@ constexpr bool test() {
return true;
}
-
constexpr bool test() {
test<int>();
test<char>();
>From cc93371390d409cc45b37e0ac249ce623aea7eb2 Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Thu, 4 Dec 2025 14:30:42 -0500
Subject: [PATCH 4/9] Allow conversions from iterator -> const_iterator
---
libcxx/include/__iterator/upper_bounded_iterator.h | 9 ++++++++-
.../optional/optional.iterator/iterator.compile.pass.cpp | 4 ++--
2 files changed, 10 insertions(+), 3 deletions(-)
diff --git a/libcxx/include/__iterator/upper_bounded_iterator.h b/libcxx/include/__iterator/upper_bounded_iterator.h
index 1ae0d11ce379f..8aa2a0340f7bc 100644
--- a/libcxx/include/__iterator/upper_bounded_iterator.h
+++ b/libcxx/include/__iterator/upper_bounded_iterator.h
@@ -11,7 +11,7 @@
* __upper_bounded_iterator is an iterator that wraps an underlying iterator.
* It stores the underlying container type to prevent mixing iterators, and allow algorithms
* to optimize based on the underlying container type.
- * It also stores the absolute maximum amount of elements the container can have, known at compile-time.
+ * It also encodes the container's (known at compile-time) maximum amount of elements as part of the type.
* As of writing, the only standard library containers which have this property are inplace_vector and optional.
*/
@@ -26,6 +26,7 @@
#include <__iterator/iterator_traits.h>
#include <__memory/pointer_traits.h>
#include <__type_traits/is_constructible.h>
+#include <__type_traits/is_convertible.h>
#include <__utility/move.h>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -59,6 +60,12 @@ class __upper_bounded_iterator {
_LIBCPP_HIDE_FROM_ABI constexpr explicit __upper_bounded_iterator(_Iter __iter) : __iter_(std::move(__iter)) {}
+ template <typename _Iter2>
+ requires is_convertible_v<_Iter2, _Iter>
+ _LIBCPP_HIDE_FROM_ABI constexpr __upper_bounded_iterator(
+ const __upper_bounded_iterator<_Iter2, _Container, _Max_Elements>& __y)
+ : __iter_(__y.__iter_) {}
+
_LIBCPP_HIDE_FROM_ABI constexpr _Iter __base() const noexcept(noexcept(_Iter(__iter_))) { return __iter_; }
_LIBCPP_HIDE_FROM_ABI constexpr auto __max_elements() const noexcept { return _Max_Elements; }
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 442195bdcb948..1d0009d3ed2d9 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
@@ -36,13 +36,13 @@ using Iter4 = std::optional<double>::const_iterator;
static_assert(std::is_convertible_v<Iter1, Iter1>);
static_assert(!std::is_convertible_v<Iter1, Iter2>);
-static_assert(!std::is_convertible_v<Iter1, Iter3>);
+static_assert(std::is_convertible_v<Iter1, Iter3>);
static_assert(!std::is_convertible_v<Iter1, Iter4>);
static_assert(std::is_convertible_v<Iter2, Iter2>);
static_assert(!std::is_convertible_v<Iter2, Iter1>);
static_assert(!std::is_convertible_v<Iter2, Iter3>);
-static_assert(!std::is_convertible_v<Iter2, Iter4>);
+static_assert(std::is_convertible_v<Iter2, Iter4>);
static_assert(std::is_convertible_v<Iter3, Iter3>);
static_assert(!std::is_convertible_v<Iter3, Iter1>);
>From f7cbb589360ed73cdbc68fbce5efd38fd367d53f Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Fri, 5 Dec 2025 18:38:09 -0500
Subject: [PATCH 5/9] Address most comments, try new name
---
libcxx/include/CMakeLists.txt | 2 +-
.../__iterator/capacity_aware_iterator.h | 180 +++++++++++++++++
.../__iterator/upper_bounded_iterator.h | 181 ------------------
libcxx/include/module.modulemap.in | 2 +-
libcxx/include/optional | 4 +-
.../iterator.compile.pass.cpp | 19 +-
6 files changed, 199 insertions(+), 189 deletions(-)
create mode 100644 libcxx/include/__iterator/capacity_aware_iterator.h
delete mode 100644 libcxx/include/__iterator/upper_bounded_iterator.h
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 587bb0dee1a08..4b74541fde0cc 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -475,6 +475,7 @@ set(files
__iterator/aliasing_iterator.h
__iterator/back_insert_iterator.h
__iterator/bounded_iter.h
+ __iterator/capacity_aware_iterator.h
__iterator/common_iterator.h
__iterator/concepts.h
__iterator/counted_iterator.h
@@ -514,7 +515,6 @@ set(files
__iterator/sortable.h
__iterator/static_bounded_iter.h
__iterator/unreachable_sentinel.h
- __iterator/upper_bounded_iterator.h
__iterator/wrap_iter.h
__locale
__locale_dir/check_grouping.h
diff --git a/libcxx/include/__iterator/capacity_aware_iterator.h b/libcxx/include/__iterator/capacity_aware_iterator.h
new file mode 100644
index 0000000000000..f006e773a39d0
--- /dev/null
+++ b/libcxx/include/__iterator/capacity_aware_iterator.h
@@ -0,0 +1,180 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___ITERATOR_UPPER_BOUNDED_ITERATOR_H
+#define _LIBCPP___ITERATOR_UPPER_BOUNDED_ITERATOR_H
+
+#include <__assert>
+#include <__compare/ordering.h>
+#include <__compare/three_way_comparable.h>
+#include <__config>
+#include <__cstddef/size_t.h>
+#include <__iterator/incrementable_traits.h>
+#include <__iterator/iterator_traits.h>
+#include <__memory/pointer_traits.h>
+#include <__type_traits/is_constructible.h>
+#include <__type_traits/is_convertible.h>
+#include <__utility/move.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+#if _LIBCPP_STD_VER >= 26
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+// __capacity_aware_iterator is an iterator that wraps an underlying iterator.
+// It stores the underlying container type to prevent mixing iterators, and allow algorithms
+// to optimize based on the underlying container type.
+// It also encodes the container's (known at compile-time) maximum amount of elements as part of the type.
+// As of writing, the only standard library containers which have this property are inplace_vector and optional.
+
+template <class _Iter, class _Container, std::size_t _ContainerMaxElements>
+class __capacity_aware_iterator {
+private:
+ _Iter __iter_;
+
+ friend _Container;
+
+public:
+ using iterator_category = iterator_traits<_Iter>::iterator_category;
+ using iterator_concept = _Iter::iterator_concept;
+ using difference_type = iter_difference_t<_Iter>;
+ using pointer = iterator_traits<_Iter>::pointer;
+ using reference = iter_reference_t<_Iter>;
+ using value_type = iter_value_t<_Iter>;
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __capacity_aware_iterator()
+ requires is_default_constructible_v<_Iter>
+ = default;
+
+ template <typename _Iter2>
+ requires is_convertible_v<_Iter2, _Iter>
+ _LIBCPP_HIDE_FROM_ABI constexpr __capacity_aware_iterator(
+ const __capacity_aware_iterator<_Iter2, _Container, _ContainerMaxElements>& __y)
+ : __iter_(__y.base()) {}
+
+private:
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __capacity_aware_iterator(_Iter __iter) : __iter_(std::move(__iter)) {}
+
+ template <typename _Tp, class>
+ friend struct __optional_iterator;
+
+public:
+ _LIBCPP_HIDE_FROM_ABI constexpr _Iter base() const noexcept { return __iter_; }
+ _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const { return *__iter_; }
+ _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator->() const
+ requires requires { __iter_.operator->(); }
+ {
+ return __iter_.operator->();
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __capacity_aware_iterator& operator++() {
+ ++__iter_;
+ return *this;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __capacity_aware_iterator operator++(int) {
+ __capacity_aware_iterator __tmp(*this);
+ ++*this;
+ return __tmp;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __capacity_aware_iterator& operator--() {
+ --__iter_;
+ return *this;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __capacity_aware_iterator operator--(int) {
+ __capacity_aware_iterator __tmp(*this);
+ --*this;
+ return __tmp;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __capacity_aware_iterator& operator+=(difference_type __n) {
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+ (__n >= 0 ? __n : -__n) <= _ContainerMaxElements,
+ "__capacity_aware_iterator::operator+=: Attempting to move iterator past its "
+ "container's possible range");
+
+ __iter_ += __n;
+ return *this;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __capacity_aware_iterator& operator-=(difference_type __n) {
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+ (__n >= 0 ? __n : -__n) <= _ContainerMaxElements,
+ "__capacity_aware_iterator::operator-=: Attempting to move iterator past its container's possible range");
+
+ __iter_ -= __n;
+ return *this;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator[](difference_type __n) const {
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+ (__n >= 0 ? __n : -__n) < _ContainerMaxElements,
+ "__capacity_aware_iterator::operator[]: Attempting to index iterator past its container's possible range");
+ return *(*this + __n);
+ }
+
+ _LIBCPP_HIDE_FROM_ABI friend constexpr bool
+ operator==(const __capacity_aware_iterator& __x, const __capacity_aware_iterator& __y) {
+ return __x.__iter_ == __y.__iter_;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI friend constexpr auto
+ operator<=>(const __capacity_aware_iterator& __x, const __capacity_aware_iterator& __y) {
+ if constexpr (three_way_comparable_with<_Iter, _Iter, strong_ordering>) {
+ return __x.__iter_ <=> __y.__iter_;
+ } else {
+ if (__x.__iter_ < __x.__iter_) {
+ return strong_ordering::less;
+ } else if (__x.__iter_ == __y.__iter_) {
+ return strong_ordering::equal;
+ }
+ return strong_ordering::greater;
+ }
+ }
+
+ _LIBCPP_HIDE_FROM_ABI friend constexpr __capacity_aware_iterator
+ operator+(const __capacity_aware_iterator& __i, difference_type __n) {
+ auto __tmp = __i;
+ __tmp += __n;
+ return __tmp;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI friend constexpr __capacity_aware_iterator
+ operator+(difference_type __n, const __capacity_aware_iterator& __i) {
+ auto __tmp = __i;
+ __tmp += __n;
+ return __tmp;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI friend constexpr __capacity_aware_iterator
+ operator-(const __capacity_aware_iterator& __i, difference_type __n) {
+ return __i.__iter_ + __n;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+ operator-(const __capacity_aware_iterator& __x, const __capacity_aware_iterator& __y) {
+ return difference_type(__x.base() - __y.base());
+ }
+};
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STD_VER >= 26
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___ITERATOR_UPPER_BOUNDED_ITERATOR_H
diff --git a/libcxx/include/__iterator/upper_bounded_iterator.h b/libcxx/include/__iterator/upper_bounded_iterator.h
deleted file mode 100644
index 8aa2a0340f7bc..0000000000000
--- a/libcxx/include/__iterator/upper_bounded_iterator.h
+++ /dev/null
@@ -1,181 +0,0 @@
-// -*- C++ -*-
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-/*
- * __upper_bounded_iterator is an iterator that wraps an underlying iterator.
- * It stores the underlying container type to prevent mixing iterators, and allow algorithms
- * to optimize based on the underlying container type.
- * It also encodes the container's (known at compile-time) maximum amount of elements as part of the type.
- * As of writing, the only standard library containers which have this property are inplace_vector and optional.
- */
-
-#ifndef _LIBCPP___ITERATOR_UPPER_BOUNDED_ITERATOR_H
-#define _LIBCPP___ITERATOR_UPPER_BOUNDED_ITERATOR_H
-
-#include <__compare/ordering.h>
-#include <__compare/three_way_comparable.h>
-#include <__config>
-#include <__cstddef/size_t.h>
-#include <__iterator/incrementable_traits.h>
-#include <__iterator/iterator_traits.h>
-#include <__memory/pointer_traits.h>
-#include <__type_traits/is_constructible.h>
-#include <__type_traits/is_convertible.h>
-#include <__utility/move.h>
-
-#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
-# pragma GCC system_header
-#endif
-
-_LIBCPP_PUSH_MACROS
-#include <__undef_macros>
-
-#if _LIBCPP_STD_VER >= 26
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-
-template <class _Iter, class _Container, std::size_t _Max_Elements>
-class __upper_bounded_iterator {
-private:
- _Iter __iter_;
-
- friend _Container;
-
-public:
- using iterator_category = iterator_traits<_Iter>::iterator_category;
- using iterator_concept = _Iter::iterator_concept;
- using value_type = iter_value_t<_Iter>;
- using difference_type = iter_difference_t<_Iter>;
- using reference = iter_reference_t<_Iter>;
-
- _LIBCPP_HIDE_FROM_ABI constexpr __upper_bounded_iterator()
- requires is_default_constructible_v<_Iter>
- = default;
-
- _LIBCPP_HIDE_FROM_ABI constexpr explicit __upper_bounded_iterator(_Iter __iter) : __iter_(std::move(__iter)) {}
-
- template <typename _Iter2>
- requires is_convertible_v<_Iter2, _Iter>
- _LIBCPP_HIDE_FROM_ABI constexpr __upper_bounded_iterator(
- const __upper_bounded_iterator<_Iter2, _Container, _Max_Elements>& __y)
- : __iter_(__y.__iter_) {}
-
- _LIBCPP_HIDE_FROM_ABI constexpr _Iter __base() const noexcept(noexcept(_Iter(__iter_))) { return __iter_; }
- _LIBCPP_HIDE_FROM_ABI constexpr auto __max_elements() const noexcept { return _Max_Elements; }
-
- _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const { return *__iter_; }
- _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator->() const
- requires requires { __iter_.operator->(); }
- {
- return __iter_.operator->();
- }
-
- _LIBCPP_HIDE_FROM_ABI constexpr __upper_bounded_iterator& operator++() {
- ++__iter_;
- return *this;
- }
-
- _LIBCPP_HIDE_FROM_ABI constexpr __upper_bounded_iterator operator++(int) {
- __upper_bounded_iterator __tmp(*this);
- ++*this;
- return __tmp;
- }
-
- _LIBCPP_HIDE_FROM_ABI constexpr __upper_bounded_iterator& operator--() {
- --__iter_;
- return *this;
- }
-
- _LIBCPP_HIDE_FROM_ABI constexpr __upper_bounded_iterator operator--(int) {
- __upper_bounded_iterator __tmp(*this);
- --*this;
- return __tmp;
- }
-
- _LIBCPP_HIDE_FROM_ABI constexpr __upper_bounded_iterator& operator+=(difference_type __x) {
- __iter_ += __x;
- return *this;
- }
-
- _LIBCPP_HIDE_FROM_ABI constexpr __upper_bounded_iterator& operator-=(difference_type __x) {
- __iter_ -= __x;
- return *this;
- }
-
- _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator[](difference_type __n) const { return *(*this + __n); }
-
- _LIBCPP_HIDE_FROM_ABI friend constexpr bool
- operator==(const __upper_bounded_iterator& __x, const __upper_bounded_iterator& __y) {
- return __x.__iter_ == __y.__iter_;
- }
-
- _LIBCPP_HIDE_FROM_ABI friend constexpr auto
- operator<=>(const __upper_bounded_iterator& __x, const __upper_bounded_iterator& __y) {
- if constexpr (three_way_comparable_with<_Iter, _Iter, strong_ordering>) {
- return __x.__iter_ <=> __y.__iter_;
- } else {
- if (__x.__iter_ < __x.__iter_) {
- return strong_ordering::less;
- } else if (__x.__iter_ == __y.__iter_) {
- return strong_ordering::equal;
- }
- return strong_ordering::greater;
- }
- }
-
- template <class _Iter2>
- _LIBCPP_HIDE_FROM_ABI friend constexpr auto
- operator<=>(const __upper_bounded_iterator& __x,
- const __upper_bounded_iterator<_Iter2, _Container, _Max_Elements> __y) {
- if constexpr (three_way_comparable_with<_Iter, _Iter2, strong_ordering>) {
- return __x.__iter_ <=> __y.__iter_;
- } else {
- if (__x.__iter_ < __x.__iter_) {
- return strong_ordering::less;
- } else if (__x.__iter_ == __y.__iter_) {
- return strong_ordering::equal;
- }
- return strong_ordering::greater;
- }
- }
-
- _LIBCPP_HIDE_FROM_ABI friend constexpr __upper_bounded_iterator
- operator+(const __upper_bounded_iterator& __i, difference_type __n) {
- auto __tmp = __i;
- __tmp += __n;
- return __tmp;
- }
-
- _LIBCPP_HIDE_FROM_ABI friend constexpr __upper_bounded_iterator
- operator+(difference_type __n, const __upper_bounded_iterator& __i) {
- auto __tmp = __i;
- __tmp += __n;
- return __tmp;
- }
-
- _LIBCPP_HIDE_FROM_ABI friend constexpr __upper_bounded_iterator
- operator-(const __upper_bounded_iterator& __i, difference_type __n) {
- return __i.__iter_ + __n;
- }
-
- template <class _Iter2>
- _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
- operator-(const __upper_bounded_iterator& __x,
- const __upper_bounded_iterator<_Iter2, _Container, _Max_Elements>& __y) {
- return difference_type(__x.__base() - __y.__base());
- }
-};
-
-_LIBCPP_END_NAMESPACE_STD
-
-#endif // _LIBCPP_STD_VER >= 26
-
-_LIBCPP_POP_MACROS
-
-#endif // _LIBCPP___ITERATOR_UPPER_BOUNDED_ITERATOR_H
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 4b3669ec703ef..ad9e745114ceb 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1543,7 +1543,7 @@ module std [system] {
module sortable { header "__iterator/sortable.h" }
module static_bounded_iter { header "__iterator/static_bounded_iter.h" }
module unreachable_sentinel { header "__iterator/unreachable_sentinel.h" }
- module upper_bounded_iterator { header "__iterator/upper_bounded_iterator.h" }
+ module capacity_aware_iterator { header "__iterator/capacity_aware_iterator.h" }
module wrap_iter { header "__iterator/wrap_iter.h" }
header "iterator"
diff --git a/libcxx/include/optional b/libcxx/include/optional
index 911ab52dccdac..1768a733bb5cb 100644
--- a/libcxx/include/optional
+++ b/libcxx/include/optional
@@ -271,7 +271,7 @@ namespace std {
# include <__functional/unary_function.h>
# include <__fwd/functional.h>
# include <__iterator/bounded_iter.h>
-# include <__iterator/upper_bounded_iterator.h>
+# include <__iterator/capacity_aware_iterator.h>
# include <__iterator/wrap_iter.h>
# include <__memory/addressof.h>
# include <__memory/construct_at.h>
@@ -690,7 +690,7 @@ private:
# if _LIBCPP_STD_VER >= 26
template <typename _Underlying>
- using __iter _LIBCPP_NODEBUG = __upper_bounded_iterator<_Underlying, __optional_iterator, 1>;
+ using __iter _LIBCPP_NODEBUG = __capacity_aware_iterator<_Underlying, optional<_Tp>, 1>;
public:
# ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
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 1d0009d3ed2d9..7db0c9ec32e87 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
@@ -15,6 +15,7 @@
#include <optional>
#include <type_traits>
+#include <vector>
template <typename T>
concept has_iterator_aliases = requires {
@@ -29,27 +30,37 @@ static_assert(has_iterator_aliases<std::optional<const int&>>);
static_assert(!has_iterator_aliases<std::optional<int (&)[1]>>);
static_assert(!has_iterator_aliases<std::optional<int (&)()>>);
-using Iter1 = std::optional<int>::iterator;
-using Iter2 = std::optional<double>::iterator;
-using Iter3 = std::optional<int>::const_iterator;
-using Iter4 = std::optional<double>::const_iterator;
+using Iter1 = std::optional<int>::iterator;
+using Iter2 = std::optional<double>::iterator;
+using Iter3 = std::optional<int>::const_iterator;
+using Iter4 = std::optional<double>::const_iterator;
+using VIter1 = std::vector<int>::iterator;
+using VIter2 = std::vector<int>::const_iterator;
static_assert(std::is_convertible_v<Iter1, Iter1>);
static_assert(!std::is_convertible_v<Iter1, Iter2>);
static_assert(std::is_convertible_v<Iter1, Iter3>);
static_assert(!std::is_convertible_v<Iter1, Iter4>);
+static_assert(!std::is_convertible_v<Iter1, VIter1>);
+static_assert(!std::is_convertible_v<Iter1, VIter2>);
static_assert(std::is_convertible_v<Iter2, Iter2>);
static_assert(!std::is_convertible_v<Iter2, Iter1>);
static_assert(!std::is_convertible_v<Iter2, Iter3>);
static_assert(std::is_convertible_v<Iter2, Iter4>);
+static_assert(!std::is_convertible_v<Iter2, VIter1>);
+static_assert(!std::is_convertible_v<Iter2, VIter2>);
static_assert(std::is_convertible_v<Iter3, Iter3>);
static_assert(!std::is_convertible_v<Iter3, Iter1>);
static_assert(!std::is_convertible_v<Iter3, Iter2>);
static_assert(!std::is_convertible_v<Iter3, Iter4>);
+static_assert(!std::is_convertible_v<Iter3, VIter1>);
+static_assert(!std::is_convertible_v<Iter3, VIter2>);
static_assert(std::is_convertible_v<Iter4, Iter4>);
static_assert(!std::is_convertible_v<Iter4, Iter1>);
static_assert(!std::is_convertible_v<Iter4, Iter2>);
static_assert(!std::is_convertible_v<Iter4, Iter3>);
+static_assert(!std::is_convertible_v<Iter4, VIter1>);
+static_assert(!std::is_convertible_v<Iter4, VIter2>);
>From 3761a5728035f27a15442dd776c6b5f6181189c4 Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Fri, 5 Dec 2025 20:20:22 -0500
Subject: [PATCH 6/9] Don't wrap bounded_iter because there would a second
redundant bounds check
---
libcxx/include/optional | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/libcxx/include/optional b/libcxx/include/optional
index 1768a733bb5cb..7b139edde82f5 100644
--- a/libcxx/include/optional
+++ b/libcxx/include/optional
@@ -694,8 +694,8 @@ private:
public:
# ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
- using iterator = __iter<__bounded_iter<__wrap_iter<__pointer>>>;
- using const_iterator = __iter<__bounded_iter<__wrap_iter<__const_pointer>>>;
+ using iterator = __bounded_iter<__wrap_iter<__pointer>>;
+ using const_iterator = __bounded_iter<__wrap_iter<__const_pointer>>;
# else
using iterator = __iter<__wrap_iter<__pointer>>;
using const_iterator = __iter<__wrap_iter<__const_pointer>>;
@@ -711,10 +711,10 @@ public:
return std::addressof(__derived_self.__get());
}();
# ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
- return iterator(std::__make_bounded_iter(
+ return std::__make_bounded_iter(
__wrap_iter<__pointer>(__ptr),
__wrap_iter<__pointer>(__ptr),
- __wrap_iter<__pointer>(__ptr) + (__derived_self.has_value() ? 1 : 0)));
+ __wrap_iter<__pointer>(__ptr) + (__derived_self.has_value() ? 1 : 0));
# else
return iterator(__wrap_iter<__pointer>(__ptr));
# endif
@@ -729,10 +729,10 @@ public:
return std::addressof(__derived_self.__get());
}();
# ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
- return const_iterator(std::__make_bounded_iter(
+ return std::__make_bounded_iter(
__wrap_iter<__const_pointer>(__ptr),
__wrap_iter<__const_pointer>(__ptr),
- __wrap_iter<__const_pointer>(__ptr) + (__derived_self.has_value() ? 1 : 0)));
+ __wrap_iter<__const_pointer>(__ptr) + (__derived_self.has_value() ? 1 : 0));
# else
return const_iterator(__wrap_iter<__const_pointer>(__ptr));
# endif
>From 88c063c0858e73ff91ff53d2a27d815c351605ee Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Fri, 5 Dec 2025 20:18:38 -0500
Subject: [PATCH 7/9] Add iterator fail test
---
.../assert.arithmetic.pass.cpp | 46 +++++++++++++++++++
.../iterator.compile.pass.cpp | 2 +
.../iterator_compare.pass.cpp | 4 +-
.../libcxx/test/features/libcxx_macros.py | 1 +
4 files changed, 51 insertions(+), 2 deletions(-)
create mode 100644 libcxx/test/libcxx/utilities/optional/optional.iterator/assert.arithmetic.pass.cpp
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
new file mode 100644
index 0000000000000..f66a3f5214343
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/optional/optional.iterator/assert.arithmetic.pass.cpp
@@ -0,0 +1,46 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <optional>
+
+// Add to iterator out of bounds.
+
+// REQUIRES: std-at-least-c++26
+// UNSUPPORTED: libcpp-hardening-mode=none, libcpp-has-abi-bounded-iterators-in-optional
+
+#include <optional>
+
+#include "check_assertion.h"
+
+int main(int, char**) {
+ {
+ std::optional<int> opt(1);
+ auto i = opt.begin();
+
+ TEST_LIBCPP_ASSERT_FAILURE(
+ i += 2,
+ "__capacity_aware_iterator::operator+=: Attempting to advance iterator past its container's possible range");
+
+ TEST_LIBCPP_ASSERT_FAILURE(
+ i += -2, "__capacity_aware_iterator::operator+=: Attempting to rewind iterator past its container's start");
+
+ TEST_LIBCPP_ASSERT_FAILURE(
+ i -= 2, "__capacity_aware_iterator::operator-=: Attempting to rewind iterator before its container's start");
+
+ TEST_LIBCPP_ASSERT_FAILURE(
+ i -= -2,
+ "__capacity_aware_iterator::operator+=: Attempting to advance iterator past its container's possible range");
+
+ TEST_LIBCPP_ASSERT_FAILURE(
+ i[2],
+ "__capacity_aware_iterator::operator[]: Attempting to index iterator past its container's possible range");
+
+ TEST_LIBCPP_ASSERT_FAILURE(
+ i[-2], "__capacity_aware_iterator::operator[]: Attempting to index iterator before its container's start");
+ }
+}
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 7db0c9ec32e87..ea99978f72e7c 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,6 +10,8 @@
// <optional>
+// UNSUPPORTED: libcpp-has-abi-bounded-iterators-in-optional
+
// template <class T> class optional::iterator;
// template <class T> class optional::const_iterator;
diff --git a/libcxx/test/std/utilities/optional/optional.iterator/iterator_compare.pass.cpp b/libcxx/test/std/utilities/optional/optional.iterator/iterator_compare.pass.cpp
index 958519d4c805c..d24300931d566 100644
--- a/libcxx/test/std/utilities/optional/optional.iterator/iterator_compare.pass.cpp
+++ b/libcxx/test/std/utilities/optional/optional.iterator/iterator_compare.pass.cpp
@@ -10,8 +10,8 @@
// <optional>
-// template <class T> class optional::iterator;
-// template <class T> class optional::const_iterator;
+// template <class T> class optional::iterator::operator<=>;
+// template <class T> class optional::const_iterator::operator<=>;
#include <cassert>
#include <compare>
diff --git a/libcxx/utils/libcxx/test/features/libcxx_macros.py b/libcxx/utils/libcxx/test/features/libcxx_macros.py
index 7a465f2e87866..71297e6a8880c 100644
--- a/libcxx/utils/libcxx/test/features/libcxx_macros.py
+++ b/libcxx/utils/libcxx/test/features/libcxx_macros.py
@@ -25,6 +25,7 @@
"_LIBCPP_ABI_VERSION": "libcpp-abi-version",
"_LIBCPP_ABI_BOUNDED_ITERATORS": "libcpp-has-abi-bounded-iterators",
"_LIBCPP_ABI_BOUNDED_ITERATORS_IN_STRING": "libcpp-has-abi-bounded-iterators-in-string",
+ "_LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL": "libcpp-has-abi-bounded-iterators-in-optional",
"_LIBCPP_ABI_BOUNDED_ITERATORS_IN_VECTOR": "libcpp-has-abi-bounded-iterators-in-vector",
"_LIBCPP_ABI_BOUNDED_ITERATORS_IN_STD_ARRAY": "libcpp-has-abi-bounded-iterators-in-std-array",
"_LIBCPP_ABI_BOUNDED_UNIQUE_PTR": "libcpp-has-abi-bounded-unique_ptr",
>From 5e9ea173bd5a4a5d8279e9f912ea049357b604ff Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Sat, 6 Dec 2025 00:44:58 -0500
Subject: [PATCH 8/9] Resolve signedness issue
---
libcxx/include/__iterator/capacity_aware_iterator.h | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/libcxx/include/__iterator/capacity_aware_iterator.h b/libcxx/include/__iterator/capacity_aware_iterator.h
index f006e773a39d0..797172337b26b 100644
--- a/libcxx/include/__iterator/capacity_aware_iterator.h
+++ b/libcxx/include/__iterator/capacity_aware_iterator.h
@@ -103,7 +103,7 @@ class __capacity_aware_iterator {
_LIBCPP_HIDE_FROM_ABI constexpr __capacity_aware_iterator& operator+=(difference_type __n) {
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
- (__n >= 0 ? __n : -__n) <= _ContainerMaxElements,
+ static_cast<size_t>((__n >= 0 ? __n : -__n)) <= _ContainerMaxElements,
"__capacity_aware_iterator::operator+=: Attempting to move iterator past its "
"container's possible range");
@@ -113,7 +113,7 @@ class __capacity_aware_iterator {
_LIBCPP_HIDE_FROM_ABI constexpr __capacity_aware_iterator& operator-=(difference_type __n) {
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
- (__n >= 0 ? __n : -__n) <= _ContainerMaxElements,
+ static_cast<size_t>((__n >= 0 ? __n : -__n)) <= _ContainerMaxElements,
"__capacity_aware_iterator::operator-=: Attempting to move iterator past its container's possible range");
__iter_ -= __n;
>From 8bbb55b9d28a740abccc9a7b23ce4b02169531ff Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Sat, 6 Dec 2025 00:45:32 -0500
Subject: [PATCH 9/9] Rename iterator_compare.pass.cpp
---
.../{iterator_compare.pass.cpp => compare.pass.cpp} | 0
1 file changed, 0 insertions(+), 0 deletions(-)
rename libcxx/test/std/utilities/optional/optional.iterator/{iterator_compare.pass.cpp => compare.pass.cpp} (100%)
diff --git a/libcxx/test/std/utilities/optional/optional.iterator/iterator_compare.pass.cpp b/libcxx/test/std/utilities/optional/optional.iterator/compare.pass.cpp
similarity index 100%
rename from libcxx/test/std/utilities/optional/optional.iterator/iterator_compare.pass.cpp
rename to libcxx/test/std/utilities/optional/optional.iterator/compare.pass.cpp
More information about the libcxx-commits
mailing list