[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