[libcxx-commits] [libcxx] d0dc3b0 - [libc++] Add a compact, bounded iterator (#208271)

via libcxx-commits libcxx-commits at lists.llvm.org
Sun Sep 20 10:45:04 PDT 2026


Author: William Tran-Viet
Date: 2026-09-20T13:44:57-04:00
New Revision: d0dc3b038f526cf85fb707cc821f4db33d3cfbcb

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

LOG: [libc++] Add a compact, bounded iterator (#208271)

- By taking advantage of unused alignment bits for a pointer of type `T`
and the static capacity known at compile time, we can make a compact
bounded iterator who's size == `sizeof(T*)` by stuffing a counter inside
those bits.
- This new iterator type is `static_packed_bounded_iterator<class _Ptr, size_t _RangeCapacity>` and is only valid if:
  - `_Ptr` is a pointer 
- `_RangeCapacity` is <= `(1 << (bit_width(alignof(T)) - 1)) - 1`. The
extra -1 is required, because we need to represent the end position past
the last element.

---------

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>

Added: 
    libcxx/include/__iterator/static_packed_bounded_iter.h
    libcxx/test/libcxx/iterators/static_packed_bounded_iter/arithmetic.pass.cpp
    libcxx/test/libcxx/iterators/static_packed_bounded_iter/assert.pass.cpp
    libcxx/test/libcxx/iterators/static_packed_bounded_iter/comparison.pass.cpp
    libcxx/test/libcxx/iterators/static_packed_bounded_iter/ctor.pass.cpp
    libcxx/test/libcxx/iterators/static_packed_bounded_iter/dereference.pass.cpp
    libcxx/test/libcxx/iterators/static_packed_bounded_iter/layout.compile.pass.cpp
    libcxx/test/libcxx/iterators/static_packed_bounded_iter/types.compile.pass.cpp
    libcxx/test/libcxx/iterators/static_packed_bounded_iter/valid.verify.cpp

Modified: 
    libcxx/include/CMakeLists.txt
    libcxx/include/module.modulemap.in

Removed: 
    


################################################################################
diff  --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index caf90b0cbbafa..252126c0ceb70 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -538,6 +538,7 @@ set(files
   __iterator/size.h
   __iterator/sortable.h
   __iterator/static_bounded_iter.h
+  __iterator/static_packed_bounded_iter.h
   __iterator/unreachable_sentinel.h
   __iterator/wrap_iter.h
   __locale_dir/check_grouping.h

diff  --git a/libcxx/include/__iterator/static_packed_bounded_iter.h b/libcxx/include/__iterator/static_packed_bounded_iter.h
new file mode 100644
index 0000000000000..41272787497ad
--- /dev/null
+++ b/libcxx/include/__iterator/static_packed_bounded_iter.h
@@ -0,0 +1,239 @@
+// -*- 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__STATIC_PACKED_BOUNDED_ITER_H
+#define _LIBCPP__STATIC_PACKED_BOUNDED_ITER_H
+
+#include <__assert>
+#include <__bit/countr.h>
+#include <__compare/ordering.h>
+#include <__compare/three_way_comparable.h>
+#include <__concepts/convertible_to.h>
+#include <__config>
+#include <__cstddef/ptr
diff _t.h>
+#include <__cstddef/size_t.h>
+#include <__iterator/concepts.h>
+#include <__iterator/incrementable_traits.h>
+#include <__iterator/iterator_traits.h>
+#include <__type_traits/is_constructible.h>
+#include <__type_traits/is_pointer.h>
+#include <cstdint>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+#if _LIBCPP_STD_VER >= 26
+
+// static_packed_bounded_iter is a bounded, contiguous iterator that is aware of its container's (compile-time) maximum
+// capacity.
+//
+// It packs the bottom bits of the pointer with an offset that tracks the iterator's current position, while
+// also incrementing/decrementing the pointer. This is to minimize the amount of instructions required to retrieve the
+// pointer, at the cost of an additional add instruction when updating.
+//
+// To retrieve the current pointer, we mask off the bottom bits and return the correctly-aligned pointer.
+//
+// This iterator requires that its container's maximum range can fit inside the representable range of
+// the available bottom bits, so (2 ^ available_bits) - 1
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+consteval bool __range_fits_in_alignment(size_t __alignment, size_t __num_elems) {
+  size_t __bits = std::countr_zero(__alignment);
+
+  // Example: For alignof(T) == 4, we have two bits free, which has a range of 0-3. We need to
+  // reserve one for the end position, so __num_elems must be < 3.
+  size_t __allowed_range = (1 << __bits) - 1;
+  return __allowed_range > __num_elems;
+}
+
+template <class _Ptr, size_t _RangeCapacity>
+class __static_packed_bounded_iterator {
+  static_assert(is_pointer_v<_Ptr>, "__static_packed_bounded_iterator requires a pointer type");
+  static_assert(std::__range_fits_in_alignment(alignof(iter_value_t<_Ptr>), _RangeCapacity),
+                "__static_packed_bounded_iterator requires the range to fit in the alignment bits");
+
+public:
+  using iterator_category = random_access_iterator_tag;
+  using iterator_concept  = contiguous_iterator_tag;
+  using 
diff erence_type   = ptr
diff _t;
+  using pointer           = iterator_traits<_Ptr>::pointer;
+  using reference         = iter_reference_t<_Ptr>;
+  using value_type        = iter_value_t<_Ptr>;
+
+private:
+  static constexpr uintptr_t __CountMask = (1 << std::countr_zero(alignof(value_type))) - 1;
+  static constexpr uintptr_t __PtrMask   = ~__CountMask;
+
+  uintptr_t __data_;
+
+  size_t __count() const noexcept { return __data_ & __CountMask; }
+
+  _Ptr __current() const noexcept { return reinterpret_cast<pointer>(__data_ & __PtrMask); }
+
+  explicit __static_packed_bounded_iterator(_Ptr __p) noexcept : __data_(reinterpret_cast<uintptr_t>(__p)) {
+    _LIBCPP_ASSERT_INTERNAL(
+        (__data_ & __CountMask) == 0, "__static_packed_bounded_iterator: Expected alignment bits of ptr to be 0");
+  }
+
+  template <class _Ptr2, size_t _RangeCapacity2>
+  friend class __static_packed_bounded_iterator;
+
+public:
+  template <class _Ptr2, size_t _RangeCapacity2>
+  friend auto __make_static_packed_bounded_iter(_Ptr2, size_t) noexcept;
+
+  __static_packed_bounded_iterator() = default;
+
+  template <convertible_to<_Ptr> _Ptr2>
+  __static_packed_bounded_iterator(const __static_packed_bounded_iterator<_Ptr2, _RangeCapacity>& __y)
+      : __data_(__y.__data_) {}
+
+  [[nodiscard]] reference operator*() const noexcept {
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+        __count() != _RangeCapacity,
+        "__static_packed_bounded_iterator::operator*: Attempt to dereference an iterator at the end");
+
+    return *__current();
+  }
+
+  pointer operator->() const noexcept {
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+        __count() != _RangeCapacity,
+        "__static_packed_bounded_iterator::operator->: Attempt to dereference an iterator at the end");
+
+    return __current();
+  }
+
+  __static_packed_bounded_iterator& operator++() noexcept {
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+        __count() != _RangeCapacity,
+        "__static_packed_bounded_iterator::operator++: Attempt to advance an iterator past the end");
+    *this += 1;
+
+    return *this;
+  }
+
+  __static_packed_bounded_iterator operator++(int) noexcept {
+    __static_packed_bounded_iterator __tmp(*this);
+    ++*this;
+    return __tmp;
+  }
+
+  __static_packed_bounded_iterator& operator--() noexcept {
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+        __count() != 0u, "__static_packed_bounded_iterator::operator--: Attempt to rewind an iterator past the start");
+    *this += -1;
+
+    return *this;
+  }
+
+  __static_packed_bounded_iterator operator--(int) noexcept {
+    __static_packed_bounded_iterator __tmp(*this);
+    --*this;
+    return __tmp;
+  }
+
+  __static_packed_bounded_iterator& operator+=(
diff erence_type __n) noexcept {
+    if (__n < 0) {
+      _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+          __count() >= static_cast<size_t>(-__n),
+          "__static_packed_bounded_iterator::operator+=: Attempt to rewind an iterator past the start");
+    } else {
+      _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+          static_cast<size_t>(__count() + __n) <= _RangeCapacity,
+          "__static_packed_bounded_iterator::operator+=: Attempt to advance an iterator past the end");
+    }
+
+    // Increment as if incrementing pointer, and the count
+    __data_ += (__n * sizeof(value_type)) + __n;
+
+    return *this;
+  }
+
+  __static_packed_bounded_iterator& operator-=(
diff erence_type __n) noexcept {
+    if (__n > 0) {
+      _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+          __count() >= static_cast<size_t>(__n),
+          "__static_packed_bounded_iterator::operator-=: Attempt to rewind an iterator past the start");
+    } else {
+      _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+          static_cast<size_t>(__count() - __n) <= _RangeCapacity,
+          "__static_packed_bounded_iterator::operator-=: Attempt to advance an iterator past the end");
+    }
+
+    *this += -__n;
+
+    return *this;
+  }
+
+  [[nodiscard]] reference operator[](
diff erence_type __n) const noexcept {
+    if (__n < 0) {
+      _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+          __count() >= static_cast<size_t>(-__n),
+          "__static_packed_bounded_iterator::operator[]: Attempt to index an iterator past the start");
+    } else {
+      _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+          static_cast<size_t>(__count() + __n) < _RangeCapacity,
+          "__static_packed_bounded_iterator::operator[]: Attempt to index an iterator at or past the end");
+    }
+    return *(*this + __n);
+  }
+
+  friend bool
+  operator==(const __static_packed_bounded_iterator& __x, const __static_packed_bounded_iterator& __y) noexcept {
+    return __x.__current() == __y.__current();
+  }
+
+  friend auto
+  operator<=>(const __static_packed_bounded_iterator& __x, const __static_packed_bounded_iterator& __y) noexcept {
+    return __x.__current() <=> __y.__current();
+  }
+
+  [[nodiscard]] friend __static_packed_bounded_iterator
+  operator+(const __static_packed_bounded_iterator& __i, 
diff erence_type __n) noexcept {
+    auto __tmp = __i;
+    __tmp += __n;
+    return __tmp;
+  }
+
+  [[nodiscard]] friend __static_packed_bounded_iterator
+  operator+(
diff erence_type __n, const __static_packed_bounded_iterator& __i) noexcept {
+    auto __tmp = __i;
+    __tmp += __n;
+    return __tmp;
+  }
+
+  [[nodiscard]] friend __static_packed_bounded_iterator
+  operator-(const __static_packed_bounded_iterator& __i, 
diff erence_type __n) noexcept {
+    auto __tmp = __i;
+    __tmp -= __n;
+    return __tmp;
+  }
+
+  [[nodiscard]] friend 
diff erence_type
+  operator-(const __static_packed_bounded_iterator& __x, const __static_packed_bounded_iterator& __y) noexcept {
+    return 
diff erence_type(__x.__current() - __y.__current());
+  }
+};
+
+template <class _Ptr, size_t _RangeCapacity>
+auto __make_static_packed_bounded_iter(_Ptr __base, size_t __offset) noexcept {
+  return __static_packed_bounded_iterator<_Ptr, _RangeCapacity>(__base) + __offset;
+}
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif
+_LIBCPP_POP_MACROS
+#endif

diff  --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 29af487336365..072280038aa58 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1587,6 +1587,7 @@ module std {
     module size                       { header "__iterator/size.h" }
     module sortable                   { header "__iterator/sortable.h" }
     module static_bounded_iter        { header "__iterator/static_bounded_iter.h" }
+    module static_packed_bounded_iter { header "__iterator/static_packed_bounded_iter.h" }
     module unreachable_sentinel       { header "__iterator/unreachable_sentinel.h" }
     module capacity_aware_iterator    { header "__iterator/capacity_aware_iterator.h" }
     module wrap_iter                  { header "__iterator/wrap_iter.h" }

diff  --git a/libcxx/test/libcxx/iterators/static_packed_bounded_iter/arithmetic.pass.cpp b/libcxx/test/libcxx/iterators/static_packed_bounded_iter/arithmetic.pass.cpp
new file mode 100644
index 0000000000000..af58bd38428bb
--- /dev/null
+++ b/libcxx/test/libcxx/iterators/static_packed_bounded_iter/arithmetic.pass.cpp
@@ -0,0 +1,114 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// template <class _Ptr, class _Tag, size_t _RangeCapacity>
+// class __static_packed_bounded_iter;
+//
+// Arithmetic operators
+
+#include <__iterator/static_packed_bounded_iter.h>
+#include <cassert>
+#include <cstddef>
+#include <iterator>
+
+struct alignas(8) Foo {
+  int x;
+
+  constexpr Foo(int y) : x(y) {}
+
+  constexpr bool operator==(const Foo& rhs) const { return x == rhs.x; }
+};
+
+template <class Iter>
+constexpr bool tests() {
+  Foo array[]       = {40, 41, 42, 43, 44};
+  Foo* b            = array + 0;
+  Foo* e            = array + 5;
+  using BoundedIter = std::__static_packed_bounded_iterator<Iter, std::size(array)>;
+  // ++it
+  {
+    BoundedIter iter    = std::__make_static_packed_bounded_iter<Iter, std::size(array)>(Iter(b), 0);
+    BoundedIter& result = ++iter;
+    assert(&result == &iter);
+    assert(*iter == 41);
+  }
+  // it++
+  {
+    BoundedIter iter   = std::__make_static_packed_bounded_iter<Iter, std::size(array)>(Iter(b), 0);
+    BoundedIter result = iter++;
+    assert(*result == 40);
+    assert(*iter == 41);
+  }
+  // --it
+  {
+    BoundedIter iter    = std::__make_static_packed_bounded_iter<Iter, std::size(array)>(Iter(b), 3);
+    BoundedIter& result = --iter;
+    assert(&result == &iter);
+    assert(*iter == 42);
+  }
+  // it--
+  {
+    BoundedIter iter   = std::__make_static_packed_bounded_iter<Iter, std::size(array)>(Iter(b), 3);
+    BoundedIter result = iter--;
+    assert(*result == 43);
+    assert(*iter == 42);
+  }
+  // it += n
+  {
+    BoundedIter iter    = std::__make_static_packed_bounded_iter<Iter, std::size(array)>(Iter(b), 0);
+    BoundedIter& result = (iter += 3);
+    assert(&result == &iter);
+    assert(*iter == 43);
+  }
+  // it + n
+  {
+    BoundedIter iter   = std::__make_static_packed_bounded_iter<Iter, std::size(array)>(Iter(b), 0);
+    BoundedIter result = iter + 3;
+    assert(*iter == 40);
+    assert(*result == 43);
+  }
+  // n + it
+  {
+    BoundedIter iter   = std::__make_static_packed_bounded_iter<Iter, std::size(array)>(Iter(b), 0);
+    BoundedIter result = 3 + iter;
+    assert(*iter == 40);
+    assert(*result == 43);
+  }
+  // it -= n
+  {
+    BoundedIter iter    = std::__make_static_packed_bounded_iter<Iter, std::size(array)>(Iter(b), 3);
+    BoundedIter& result = (iter -= 3);
+    assert(&result == &iter);
+    assert(*iter == 40);
+  }
+  // it - n
+  {
+    BoundedIter iter   = std::__make_static_packed_bounded_iter<Iter, std::size(array)>(Iter(b), 3);
+    BoundedIter result = iter - 3;
+    assert(*iter == 43);
+    assert(*result == 40);
+  }
+  // it - it
+  {
+    BoundedIter iter1     = std::__make_static_packed_bounded_iter<Iter, std::size(array)>(Iter(b), 0);
+    BoundedIter iter2     = std::__make_static_packed_bounded_iter<Iter, std::size(array)>(Iter(e), 0);
+    std::ptr
diff _t result = iter2 - iter1;
+    assert(result == 5);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  tests<Foo*>();
+  // static_assert(tests<Foo*>()); TODO: This type is not constexpr.
+
+  return 0;
+}

diff  --git a/libcxx/test/libcxx/iterators/static_packed_bounded_iter/assert.pass.cpp b/libcxx/test/libcxx/iterators/static_packed_bounded_iter/assert.pass.cpp
new file mode 100644
index 0000000000000..28d15b24a45a0
--- /dev/null
+++ b/libcxx/test/libcxx/iterators/static_packed_bounded_iter/assert.pass.cpp
@@ -0,0 +1,75 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// REQUIRES: can-test-hardening-assertions-fast
+
+// template <class _Ptr, class _Tag, class _RangeCapacity>
+// class __static_packed_bounded_iterator;
+
+// Check assert failure if advancing, rewinding past start or past end, and dereferencing if at end
+
+#include <__iterator/static_packed_bounded_iter.h>
+#include <iterator>
+
+#include "check_assertion.h"
+#include "test_macros.h"
+
+struct TEST_ALIGNAS(4) Foo {
+  char x;
+  Foo(int y) : x(y) {}
+};
+
+template <typename Ptr, typename Ty = Foo>
+void test() {
+  Ty arr[] = {1, 2};
+
+  constexpr long sz = std::size(arr);
+
+  using BoundedIter = std::__static_packed_bounded_iterator<Ptr, sz>;
+
+  BoundedIter it = std::__make_static_packed_bounded_iter<Ptr, sz>(Ptr(arr), 0);
+
+  TEST_LIBCPP_ASSERT_FAILURE(
+      it--, "__static_packed_bounded_iterator::operator--: Attempt to rewind an iterator past the start");
+  TEST_LIBCPP_ASSERT_FAILURE(
+      --it, "__static_packed_bounded_iterator::operator--: Attempt to rewind an iterator past the start");
+
+  TEST_LIBCPP_ASSERT_FAILURE(
+      it += -1, "__static_packed_bounded_iterator::operator+=: Attempt to rewind an iterator past the start");
+  TEST_LIBCPP_ASSERT_FAILURE(
+      it += (sz + 1), "__static_packed_bounded_iterator::operator+=: Attempt to advance an iterator past the end");
+
+  TEST_LIBCPP_ASSERT_FAILURE(
+      it -= 1, "__static_packed_bounded_iterator::operator-=: Attempt to rewind an iterator past the start");
+  TEST_LIBCPP_ASSERT_FAILURE(
+      it -= -(sz + 1), "__static_packed_bounded_iterator::operator-=: Attempt to advance an iterator past the end");
+
+  TEST_LIBCPP_ASSERT_FAILURE(
+      it[sz], "__static_packed_bounded_iterator::operator[]: Attempt to index an iterator at or past the end");
+  TEST_LIBCPP_ASSERT_FAILURE(
+      it[-1], "__static_packed_bounded_iterator::operator[]: Attempt to index an iterator past the start");
+
+  it += sz;
+
+  TEST_LIBCPP_ASSERT_FAILURE(
+      it++, "__static_packed_bounded_iterator::operator++: Attempt to advance an iterator past the end");
+  TEST_LIBCPP_ASSERT_FAILURE(
+      ++it, "__static_packed_bounded_iterator::operator++: Attempt to advance an iterator past the end");
+
+  TEST_LIBCPP_ASSERT_FAILURE(
+      *it, "__static_packed_bounded_iterator::operator*: Attempt to dereference an iterator at the end");
+  TEST_LIBCPP_ASSERT_FAILURE(
+      it.operator->(), "__static_packed_bounded_iterator::operator->: Attempt to dereference an iterator at the end");
+}
+
+int main(int, char**) {
+  test<Foo*>();
+
+  return 0;
+}

diff  --git a/libcxx/test/libcxx/iterators/static_packed_bounded_iter/comparison.pass.cpp b/libcxx/test/libcxx/iterators/static_packed_bounded_iter/comparison.pass.cpp
new file mode 100644
index 0000000000000..6a6a477efd673
--- /dev/null
+++ b/libcxx/test/libcxx/iterators/static_packed_bounded_iter/comparison.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
+
+// template <class _Ptr, class _Tag, size_t _RangeCapacity>
+// class __static_packed_bounded_iter;
+//
+// Comparison operators
+
+#include <__iterator/static_packed_bounded_iter.h>
+#include <cassert>
+#include <compare>
+#include <concepts>
+#include <iterator>
+
+#include "test_macros.h"
+
+struct alignas(4) Foo {
+  int x;
+  constexpr Foo(int y) : x(y) {}
+};
+
+template <class Iter>
+constexpr bool tests() {
+  Foo array[]             = {0, 1};
+  Foo* b                  = array + 0;
+  using BoundedIter       = std::__static_packed_bounded_iterator<Iter, std::size(array)>;
+  BoundedIter const iter1 = std::__make_static_packed_bounded_iter<Iter, std::size(array)>(Iter(b), 0);
+  BoundedIter const iter2 = std::__make_static_packed_bounded_iter<Iter, std::size(array)>(Iter(b), 2);
+
+  // operator==
+  {
+    assert(iter1 == iter1);
+    assert(!(iter1 == iter2));
+  }
+  // operator!=
+  {
+    assert(iter1 != iter2);
+    assert(!(iter1 != iter1));
+  }
+  // operator<
+  {
+    assert(iter1 < iter2);
+    assert(!(iter2 < iter1));
+    assert(!(iter1 < iter1));
+  }
+  // operator>
+  {
+    assert(iter2 > iter1);
+    assert(!(iter1 > iter2));
+    assert(!(iter1 > iter1));
+  }
+  // operator<=
+  {
+    assert(iter1 <= iter2);
+    assert(!(iter2 <= iter1));
+    assert(iter1 <= iter1);
+  }
+  // operator>=
+  {
+    assert(iter2 >= iter1);
+    assert(!(iter1 >= iter2));
+    assert(iter1 >= iter1);
+  }
+
+  std::same_as<std::strong_ordering> decltype(auto) r1 = iter1 <=> iter2;
+  assert(r1 == std::strong_ordering::less);
+
+  return true;
+}
+
+// Although this isn't necessarily the case anymore, the original design had a 
diff erent representation in constexpr vs
+// runtime, which had an issue where if you had an iterator that was modified in constexpr, and compared it to a
+// runtime iterator that was modified in the same way but at runtime, you'd run into a situation where two iterators
+// which should compare equal, don't, because their underlying values are 
diff erent despite having the same operations
+// done to it.
+// So, have a test case to "emulate" this behaviour, so that it can be updated once __static_packed_bounded_iter is
+// constexpr friendly rather than forgetting this may have been an issue, now that it technically isn't possible.
+
+static constinit Foo array[] = {3, 4};
+using It                     = std::__static_packed_bounded_iterator<Foo*, std::size(array)>;
+static It iter1              = std::__make_static_packed_bounded_iter<Foo*, std::size(array)>(array, 1);
+
+constexpr void test2(It a, It b) { assert(a == b); }
+
+int main(int, char**) {
+  tests<Foo*>();
+  // static_assert(tests<Foo*>(), ""); TODO: This type is not constexpr.
+
+  test2(iter1, std::__make_static_packed_bounded_iter<Foo*, std::size(array)>(array, 1));
+
+  return 0;
+}

diff  --git a/libcxx/test/libcxx/iterators/static_packed_bounded_iter/ctor.pass.cpp b/libcxx/test/libcxx/iterators/static_packed_bounded_iter/ctor.pass.cpp
new file mode 100644
index 0000000000000..5940124399420
--- /dev/null
+++ b/libcxx/test/libcxx/iterators/static_packed_bounded_iter/ctor.pass.cpp
@@ -0,0 +1,57 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// template <class _Ptr, class _Tag, size_t _RangeCapacity>
+// class __static_packed_bounded_iter;
+
+#include <__iterator/static_packed_bounded_iter.h>
+#include <cassert>
+#include <cstddef>
+#include <type_traits>
+
+struct Base {
+  long x = 0;
+};
+
+struct Derived : public Base {
+  long y;
+
+  constexpr Derived(long yy) : Base(1), y(yy) {}
+};
+
+template <typename T, std::size_t _Capacity>
+using Iter = std::__static_packed_bounded_iterator<T, _Capacity>;
+
+constexpr bool test() {
+  {
+    Derived a[1] = {{2}};
+    auto it      = std::__make_static_packed_bounded_iter<Derived*, 1>(a, 0);
+
+    assert(it->x == 1);
+    assert(it->y == 2);
+
+    static_assert(std::is_convertible_v<Derived*, Base*>);
+
+    auto it2 = std::__static_packed_bounded_iterator<Base*, 1>{it};
+    assert(it2->x == 1);
+  }
+
+  static_assert(!std::is_constructible_v<Iter<Derived*, 1>, Iter<Base*, 1>>);
+  static_assert(!std::is_constructible_v<Iter<int*, 1>, Iter<long*, 1>>);
+
+  return true;
+}
+
+int main(int, char**) {
+  assert(test());
+  // static_assert(test());
+
+  return 0;
+}

diff  --git a/libcxx/test/libcxx/iterators/static_packed_bounded_iter/dereference.pass.cpp b/libcxx/test/libcxx/iterators/static_packed_bounded_iter/dereference.pass.cpp
new file mode 100644
index 0000000000000..c6ea66123b5db
--- /dev/null
+++ b/libcxx/test/libcxx/iterators/static_packed_bounded_iter/dereference.pass.cpp
@@ -0,0 +1,54 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// template <class _Ptr, class _Tag, size_t _RangeCapacity>
+// class __static_packed_bounded_iter;
+//
+// Dereference and indexing operators
+
+// REQUIRES: std-at-least-c++26
+
+#include <__iterator/static_packed_bounded_iter.h>
+#include <cassert>
+#include <iterator>
+
+struct alignas(8) Foo {
+  char x;
+  constexpr bool operator==(Foo const& other) const { return x == other.x; }
+};
+
+template <class Iter>
+constexpr bool tests() {
+  Foo array[] = {Foo{40}, Foo{41}, Foo{42}, Foo{43}, Foo{44}};
+  Foo* b      = array + 0;
+
+  using BoundedIter = std::__static_packed_bounded_iterator<Foo*, std::size(array)>;
+
+  BoundedIter const iter1 = std::__make_static_packed_bounded_iter<Foo*, std::size(array)>(Iter(b), 0);
+  BoundedIter const iter2 = std::__make_static_packed_bounded_iter<Foo*, std::size(array)>(Iter(b), 5);
+
+  // operator*
+  assert(*iter1 == Foo{40});
+  // operator->
+  assert(iter1->x == 40);
+  // operator[]
+  assert(iter1[0] == Foo{40});
+  assert(iter1[1] == Foo{41});
+  assert(iter1[2] == Foo{42});
+  assert(iter2[-1] == Foo{44});
+  assert(iter2[-2] == Foo{43});
+
+  return true;
+}
+
+int main(int, char**) {
+  tests<Foo*>();
+  // static_assert(tests<Foo*>(), "");
+
+  return 0;
+}

diff  --git a/libcxx/test/libcxx/iterators/static_packed_bounded_iter/layout.compile.pass.cpp b/libcxx/test/libcxx/iterators/static_packed_bounded_iter/layout.compile.pass.cpp
new file mode 100644
index 0000000000000..14b2b6685c935
--- /dev/null
+++ b/libcxx/test/libcxx/iterators/static_packed_bounded_iter/layout.compile.pass.cpp
@@ -0,0 +1,28 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// template <class _Ptr, class _Tag, class _RangeCapacity>
+// class __static_packed_bounded_iterator;
+//
+// Make sure that the this static_packed_bounded_iterator occupies as much memory as a pointer
+
+#include <__iterator/static_packed_bounded_iter.h>
+#include <cstddef>
+#include <type_traits>
+
+using Iter = std::__static_packed_bounded_iterator<int*, 2>;
+
+static_assert(sizeof(Iter) == sizeof(void*));
+static_assert(alignof(Iter) == alignof(void*));
+static_assert(std::is_trivially_copyable_v<Iter>);
+static_assert(std::is_trivially_copy_assignable_v<Iter>);
+static_assert(std::is_trivially_move_constructible_v<Iter>);
+static_assert(std::is_trivially_move_assignable_v<Iter>);
+static_assert(std::is_trivially_destructible_v<Iter>);

diff  --git a/libcxx/test/libcxx/iterators/static_packed_bounded_iter/types.compile.pass.cpp b/libcxx/test/libcxx/iterators/static_packed_bounded_iter/types.compile.pass.cpp
new file mode 100644
index 0000000000000..7bc4982005a33
--- /dev/null
+++ b/libcxx/test/libcxx/iterators/static_packed_bounded_iter/types.compile.pass.cpp
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// template <class _Ptr, class _Tag, class _RangeCapacity>
+// class __static_packed_bounded_iterator;
+//
+// Nested types
+
+#include <__iterator/static_packed_bounded_iter.h>
+#include <cstddef>
+#include <iterator>
+#include <type_traits>
+
+using Iter = std::__static_packed_bounded_iterator<int*, 2>;
+
+static_assert(std::is_same_v<Iter::value_type, int>, "");
+static_assert(std::is_same_v<Iter::
diff erence_type, std::ptr
diff _t>, "");
+static_assert(std::is_same_v<Iter::pointer, int*>, "");
+static_assert(std::is_same_v<Iter::reference, int&>, "");
+static_assert(std::is_same_v<Iter::iterator_category, std::random_access_iterator_tag>, "");
+static_assert(std::is_same_v<Iter::iterator_concept, std::contiguous_iterator_tag>, "");
+static_assert(std::contiguous_iterator<Iter>);

diff  --git a/libcxx/test/libcxx/iterators/static_packed_bounded_iter/valid.verify.cpp b/libcxx/test/libcxx/iterators/static_packed_bounded_iter/valid.verify.cpp
new file mode 100644
index 0000000000000..421d0160bf2da
--- /dev/null
+++ b/libcxx/test/libcxx/iterators/static_packed_bounded_iter/valid.verify.cpp
@@ -0,0 +1,34 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// template <class _Ptr, size_t _RangeCapacity>
+// class __static_packed_bounded_iterator;
+
+#include <__iterator/static_packed_bounded_iter.h>
+#include <cstddef>
+#include <cstdint>
+
+#include "test_iterators.h"
+
+void test() {
+  std::__static_packed_bounded_iterator<std::int8_t*, 1>
+      v1; // expected-error-re@*:* {{static assertion failed{{.*}}: __static_packed_bounded_iterator requires the range to fit in the alignment bits}}
+  std::__static_packed_bounded_iterator<std::int16_t*, 1>
+      v2; // expected-error-re@*:* {{static assertion failed{{.*}}: __static_packed_bounded_iterator requires the range to fit in the alignment bits}}
+  std::__static_packed_bounded_iterator<std::int32_t*, 3>
+      v3; // expected-error-re@*:* {{static assertion failed{{.*}}: __static_packed_bounded_iterator requires the range to fit in the alignment bits}}
+  std::__static_packed_bounded_iterator<std::int64_t*, 7>
+      v4; // expected-error-re@*:* {{static assertion failed{{.*}}: __static_packed_bounded_iterator requires the range to fit in the alignment bits}}
+
+  std::__static_packed_bounded_iterator<cpp20_random_access_iterator<int*>, 0>
+      v5; // expected-error-re@*:* {{static assertion failed{{.*}}: __static_packed_bounded_iterator requires a pointer type}}
+  std::__static_packed_bounded_iterator<contiguous_iterator<int*>, 0>
+      v6; // expected-error-re@*:* {{static assertion failed{{.*}}: __static_packed_bounded_iterator requires a pointer type}}
+}


        


More information about the libcxx-commits mailing list