Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[libc++] Add support for bounded iterators in std::array #110729

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

ldionne
Copy link
Member

@ldionne ldionne commented Oct 1, 2024

This patch introduces a new kind of bounded iterator that knows the size of its valid range at compile-time, as in
std::array. This allows computing the end of the range from the start of the range and the size, which requires
storing only the start of the range in the iterator instead of both the start and the size (or start and end).
The iterator wrapper is otherwise identical in design to the existing __bounded_iter.

Since this requires changing the type of the iterators returned by std::array, this new bounded iterator is
controlled by an ABI flag.

As a drive-by, centralize the tests for std::array::operator[] and add missing tests for OOB operator[] on
non-empty arrays.

Fixes #70864

This patch introduces a new kind of bounded iterator that knows the
size of its valid range at compile-time, as in std::array. This allows
computing the end of the range from the start of the range (and the
size) instead of storing the size (or the end of the range) in the
iterator. The iterator wrapper is otherwise identical in design to the
existing __bounded_iter.

Since this requires changing the type of the iterators returned by
std::array, this new bounded iterator is controlled by an ABI flag.

As a drive-by, centralize the tests for std::array::operator[] and
add missing tests for OOB operator[] on non-empty arrays.

Fixes llvm#70864
@ldionne ldionne added the hardening Issues related to the hardening effort label Oct 1, 2024
@ldionne ldionne requested a review from var-const October 1, 2024 19:52
@ldionne ldionne requested a review from a team as a code owner October 1, 2024 19:52
@llvmbot llvmbot added the libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. label Oct 1, 2024
@llvmbot
Copy link
Collaborator

llvmbot commented Oct 1, 2024

@llvm/pr-subscribers-libcxx

Author: Louis Dionne (ldionne)

Changes

This patch introduces a new kind of bounded iterator that knows the size of its valid range at compile-time, as in std::array. This allows computing the end of the range from the start of the range (and the size) instead of storing the size (or the end of the range) in the iterator. The iterator wrapper is otherwise identical in design to the existing __bounded_iter.

Since this requires changing the type of the iterators returned by std::array, this new bounded iterator is controlled by an ABI flag.

As a drive-by, centralize the tests for std::array::operator[] and add missing tests for OOB operator[] on non-empty arrays.

Fixes #70864


Patch is 32.89 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/110729.diff

13 Files Affected:

  • (modified) libcxx/cmake/caches/Generic-hardening-mode-fast-with-abi-breaks.cmake (+8-1)
  • (modified) libcxx/include/CMakeLists.txt (+1)
  • (modified) libcxx/include/__configuration/abi.h (+6)
  • (modified) libcxx/include/__iterator/bounded_iter.h (+2-2)
  • (added) libcxx/include/__iterator/static_bounded_iter.h (+268)
  • (modified) libcxx/include/array (+53-6)
  • (modified) libcxx/include/module.modulemap (+1)
  • (removed) libcxx/test/libcxx/containers/sequences/array/array.zero/assert.subscript.pass.cpp (-41)
  • (renamed) libcxx/test/std/containers/sequences/array/assert.back.pass.cpp ()
  • (renamed) libcxx/test/std/containers/sequences/array/assert.front.pass.cpp (+1-1)
  • (added) libcxx/test/std/containers/sequences/array/assert.indexing.pass.cpp (+79)
  • (added) libcxx/test/std/containers/sequences/array/assert.iterators.pass.cpp (+145)
  • (modified) libcxx/utils/libcxx/test/features.py (+1)
diff --git a/libcxx/cmake/caches/Generic-hardening-mode-fast-with-abi-breaks.cmake b/libcxx/cmake/caches/Generic-hardening-mode-fast-with-abi-breaks.cmake
index f63436c7679478..699d3f8866861c 100644
--- a/libcxx/cmake/caches/Generic-hardening-mode-fast-with-abi-breaks.cmake
+++ b/libcxx/cmake/caches/Generic-hardening-mode-fast-with-abi-breaks.cmake
@@ -1,2 +1,9 @@
 set(LIBCXX_HARDENING_MODE "fast" CACHE STRING "")
-set(LIBCXX_ABI_DEFINES "_LIBCPP_ABI_BOUNDED_ITERATORS;_LIBCPP_ABI_BOUNDED_ITERATORS_IN_STRING;_LIBCPP_ABI_BOUNDED_ITERATORS_IN_VECTOR;_LIBCPP_ABI_BOUNDED_UNIQUE_PTR" CACHE STRING "")
+set(_defines
+    _LIBCPP_ABI_BOUNDED_ITERATORS
+    _LIBCPP_ABI_BOUNDED_ITERATORS_IN_STRING
+    _LIBCPP_ABI_BOUNDED_ITERATORS_IN_VECTOR
+    _LIBCPP_ABI_BOUNDED_UNIQUE_PTR
+    _LIBCPP_ABI_BOUNDED_ITERATORS_IN_STD_ARRAY
+)
+set(LIBCXX_ABI_DEFINES "${_defines}" CACHE STRING "")
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 8a63280053340f..19b1fcd1f957a0 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -486,6 +486,7 @@ set(files
   __iterator/segmented_iterator.h
   __iterator/size.h
   __iterator/sortable.h
+  __iterator/static_bounded_iter.h
   __iterator/unreachable_sentinel.h
   __iterator/wrap_iter.h
   __locale
diff --git a/libcxx/include/__configuration/abi.h b/libcxx/include/__configuration/abi.h
index 62c129f5921dee..17623ca491c185 100644
--- a/libcxx/include/__configuration/abi.h
+++ b/libcxx/include/__configuration/abi.h
@@ -176,6 +176,12 @@
 // ABI impact: changes the iterator type of `vector` (except `vector<bool>`).
 // #define _LIBCPP_ABI_BOUNDED_ITERATORS_IN_VECTOR
 
+// Changes the iterator type of `array` to a bounded iterator that keeps track of whether it's within the bounds of the
+// container and asserts it on every dereference and when performing iterator arithmetic.
+//
+// ABI impact: changes the iterator type of `array`.
+// #define _LIBCPP_ABI_BOUNDED_ITERATORS_IN_STD_ARRAY
+
 // [[msvc::no_unique_address]] seems to mostly affect empty classes, so the padding scheme for Itanium doesn't work.
 #if defined(_LIBCPP_ABI_MICROSOFT) && !defined(_LIBCPP_ABI_NO_COMPRESSED_PAIR_PADDING)
 #  define _LIBCPP_ABI_NO_COMPRESSED_PAIR_PADDING
diff --git a/libcxx/include/__iterator/bounded_iter.h b/libcxx/include/__iterator/bounded_iter.h
index 5a86bd98e71940..4811b0dde7f777 100644
--- a/libcxx/include/__iterator/bounded_iter.h
+++ b/libcxx/include/__iterator/bounded_iter.h
@@ -60,8 +60,8 @@ struct __bounded_iter {
 
   // Create a singular iterator.
   //
-  // Such an iterator points past the end of an empty span, so it is not dereferenceable.
-  // Observing operations like comparison and assignment are valid.
+  // Such an iterator points past the end of an empty range, so it is not dereferenceable.
+  // Operations like comparison and assignment are valid.
   _LIBCPP_HIDE_FROM_ABI __bounded_iter() = default;
 
   _LIBCPP_HIDE_FROM_ABI __bounded_iter(__bounded_iter const&) = default;
diff --git a/libcxx/include/__iterator/static_bounded_iter.h b/libcxx/include/__iterator/static_bounded_iter.h
new file mode 100644
index 00000000000000..c322014447099f
--- /dev/null
+++ b/libcxx/include/__iterator/static_bounded_iter.h
@@ -0,0 +1,268 @@
+// -*- 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_STATIC_BOUNDED_ITER_H
+#define _LIBCPP___ITERATOR_STATIC_BOUNDED_ITER_H
+
+#include <__assert>
+#include <__compare/ordering.h>
+#include <__compare/three_way_comparable.h>
+#include <__config>
+#include <__cstddef/size_t.h>
+#include <__iterator/iterator_traits.h>
+#include <__memory/pointer_traits.h>
+#include <__type_traits/enable_if.h>
+#include <__type_traits/integral_constant.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>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+// Iterator wrapper that carries the valid range it is allowed to access, but where
+// the size of that range is known at compile-time.
+//
+// This is an iterator wrapper for contiguous iterators that points within a range
+// whose size is known at compile-time. This is very similar to `__bounded_iter`,
+// except that we don't have to store the end of the range in physical memory since
+// it can be computed from the start of the range.
+//
+// The operations on which this iterator wrapper traps are the same as `__bounded_iter`.
+template <class _Iterator, size_t _Size, class = __enable_if_t< __libcpp_is_contiguous_iterator<_Iterator>::value> >
+struct __static_bounded_iter {
+  using value_type        = typename iterator_traits<_Iterator>::value_type;
+  using difference_type   = typename iterator_traits<_Iterator>::difference_type;
+  using pointer           = typename iterator_traits<_Iterator>::pointer;
+  using reference         = typename iterator_traits<_Iterator>::reference;
+  using iterator_category = typename iterator_traits<_Iterator>::iterator_category;
+#if _LIBCPP_STD_VER >= 20
+  using iterator_concept = contiguous_iterator_tag;
+#endif
+
+  // Create a singular iterator.
+  //
+  // Such an iterator points past the end of an empty range, so it is not dereferenceable.
+  // Operations like comparison and assignment are valid.
+  _LIBCPP_HIDE_FROM_ABI __static_bounded_iter() = default;
+
+  _LIBCPP_HIDE_FROM_ABI __static_bounded_iter(__static_bounded_iter const&) = default;
+  _LIBCPP_HIDE_FROM_ABI __static_bounded_iter(__static_bounded_iter&&)      = default;
+
+  template <class _OtherIterator, __enable_if_t<is_convertible<_OtherIterator, _Iterator>::value, int> = 0>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR
+  __static_bounded_iter(__static_bounded_iter<_OtherIterator, _Size> const& __other) _NOEXCEPT
+      : __current_(__other.__current_),
+        __begin_(__other.__begin_) {}
+
+  // Assign a bounded iterator to another one, rebinding the bounds of the iterator as well.
+  _LIBCPP_HIDE_FROM_ABI __static_bounded_iter& operator=(__static_bounded_iter const&) = default;
+  _LIBCPP_HIDE_FROM_ABI __static_bounded_iter& operator=(__static_bounded_iter&&)      = default;
+
+private:
+  // Create an iterator wrapping the given iterator, and whose bounds are described
+  // by the provided [begin, begin + _Size] range.
+  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit __static_bounded_iter(_Iterator __current, _Iterator __begin)
+      : __current_(__current), __begin_(__begin) {
+    _LIBCPP_ASSERT_INTERNAL(
+        __begin <= __current, "__static_bounded_iter(current, begin): current and begin are inconsistent");
+    _LIBCPP_ASSERT_INTERNAL(
+        __current <= __end(), "__static_bounded_iter(current, begin): current and (begin + Size) are inconsistent");
+  }
+
+  template <size_t _Sz, class _It>
+  friend _LIBCPP_CONSTEXPR __static_bounded_iter<_It, _Sz> __make_static_bounded_iter(_It, _It);
+
+public:
+  // Dereference and indexing operations.
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 reference operator*() const _NOEXCEPT {
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+        __current_ != __end(), "__static_bounded_iter::operator*: Attempt to dereference an iterator at the end");
+    return *__current_;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 pointer operator->() const _NOEXCEPT {
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+        __current_ != __end(), "__static_bounded_iter::operator->: Attempt to dereference an iterator at the end");
+    return std::__to_address(__current_);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 reference operator[](difference_type __n) const _NOEXCEPT {
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+        __n >= __begin_ - __current_, "__static_bounded_iter::operator[]: Attempt to index an iterator past the start");
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+        __n < __end() - __current_,
+        "__static_bounded_iter::operator[]: Attempt to index an iterator at or past the end");
+    return __current_[__n];
+  }
+
+  // Arithmetic operations.
+  //
+  // These operations check that the iterator remains within `[begin, end]`.
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 __static_bounded_iter& operator++() _NOEXCEPT {
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+        __current_ != __end(), "__static_bounded_iter::operator++: Attempt to advance an iterator past the end");
+    ++__current_;
+    return *this;
+  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 __static_bounded_iter operator++(int) _NOEXCEPT {
+    __static_bounded_iter __tmp(*this);
+    ++*this;
+    return __tmp;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 __static_bounded_iter& operator--() _NOEXCEPT {
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+        __current_ != __begin_, "__static_bounded_iter::operator--: Attempt to rewind an iterator past the start");
+    --__current_;
+    return *this;
+  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 __static_bounded_iter operator--(int) _NOEXCEPT {
+    __static_bounded_iter __tmp(*this);
+    --*this;
+    return __tmp;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 __static_bounded_iter& operator+=(difference_type __n) _NOEXCEPT {
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+        __n >= __begin_ - __current_,
+        "__static_bounded_iter::operator+=: Attempt to rewind an iterator past the start");
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+        __n <= __end() - __current_, "__static_bounded_iter::operator+=: Attempt to advance an iterator past the end");
+    __current_ += __n;
+    return *this;
+  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 friend __static_bounded_iter
+  operator+(__static_bounded_iter const& __self, difference_type __n) _NOEXCEPT {
+    __static_bounded_iter __tmp(__self);
+    __tmp += __n;
+    return __tmp;
+  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 friend __static_bounded_iter
+  operator+(difference_type __n, __static_bounded_iter const& __self) _NOEXCEPT {
+    __static_bounded_iter __tmp(__self);
+    __tmp += __n;
+    return __tmp;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 __static_bounded_iter& operator-=(difference_type __n) _NOEXCEPT {
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+        __n <= __current_ - __begin_,
+        "__static_bounded_iter::operator-=: Attempt to rewind an iterator past the start");
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+        __n >= __current_ - __end(), "__static_bounded_iter::operator-=: Attempt to advance an iterator past the end");
+    __current_ -= __n;
+    return *this;
+  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 friend __static_bounded_iter
+  operator-(__static_bounded_iter const& __self, difference_type __n) _NOEXCEPT {
+    __static_bounded_iter __tmp(__self);
+    __tmp -= __n;
+    return __tmp;
+  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 friend difference_type
+  operator-(__static_bounded_iter const& __x, __static_bounded_iter const& __y) _NOEXCEPT {
+    return __x.__current_ - __y.__current_;
+  }
+
+  // Comparison operations.
+  //
+  // These operations do not check whether the iterators are within their bounds.
+  // The valid range for each iterator is also not considered as part of the comparison,
+  // i.e. two iterators pointing to the same location will be considered equal even
+  // if they have different validity ranges.
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR friend bool
+  operator==(__static_bounded_iter const& __x, __static_bounded_iter const& __y) _NOEXCEPT {
+    return __x.__current_ == __y.__current_;
+  }
+
+#if _LIBCPP_STD_VER <= 17
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR friend bool
+  operator!=(__static_bounded_iter const& __x, __static_bounded_iter const& __y) _NOEXCEPT {
+    return __x.__current_ != __y.__current_;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR friend bool
+  operator<(__static_bounded_iter const& __x, __static_bounded_iter const& __y) _NOEXCEPT {
+    return __x.__current_ < __y.__current_;
+  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR friend bool
+  operator>(__static_bounded_iter const& __x, __static_bounded_iter const& __y) _NOEXCEPT {
+    return __x.__current_ > __y.__current_;
+  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR friend bool
+  operator<=(__static_bounded_iter const& __x, __static_bounded_iter const& __y) _NOEXCEPT {
+    return __x.__current_ <= __y.__current_;
+  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR friend bool
+  operator>=(__static_bounded_iter const& __x, __static_bounded_iter const& __y) _NOEXCEPT {
+    return __x.__current_ >= __y.__current_;
+  }
+
+#else
+  _LIBCPP_HIDE_FROM_ABI constexpr friend strong_ordering
+  operator<=>(__static_bounded_iter const& __x, __static_bounded_iter const& __y) noexcept {
+    if constexpr (three_way_comparable<_Iterator, strong_ordering>) {
+      return __x.__current_ <=> __y.__current_;
+    } else {
+      if (__x.__current_ < __y.__current_)
+        return strong_ordering::less;
+
+      if (__x.__current_ == __y.__current_)
+        return strong_ordering::equal;
+
+      return strong_ordering::greater;
+    }
+  }
+#endif // _LIBCPP_STD_VER >= 20
+
+private:
+  template <class>
+  friend struct pointer_traits;
+  template <class, size_t, class>
+  friend struct __static_bounded_iter;
+  _Iterator __current_; // current iterator
+  _Iterator __begin_;   // start of the valid range, which is [__begin_, __begin_ + _Size)
+
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Iterator __end() const _NOEXCEPT { return __begin_ + _Size; }
+};
+
+template <size_t _Size, class _It>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __static_bounded_iter<_It, _Size>
+__make_static_bounded_iter(_It __it, _It __begin) {
+  return __static_bounded_iter<_It, _Size>(std::move(__it), std::move(__begin));
+}
+
+#if _LIBCPP_STD_VER <= 17
+template <class _Iterator, size_t _Size>
+struct __libcpp_is_contiguous_iterator<__static_bounded_iter<_Iterator, _Size> > : true_type {};
+#endif
+
+template <class _Iterator, size_t _Size>
+struct pointer_traits<__static_bounded_iter<_Iterator, _Size> > {
+  using pointer         = __static_bounded_iter<_Iterator, _Size>;
+  using element_type    = typename pointer_traits<_Iterator>::element_type;
+  using difference_type = typename pointer_traits<_Iterator>::difference_type;
+
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR static element_type* to_address(pointer __it) _NOEXCEPT {
+    return std::__to_address(__it.__current_);
+  }
+};
+
+_LIBCPP_END_NAMESPACE_STD
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___ITERATOR_STATIC_BOUNDED_ITER_H
diff --git a/libcxx/include/array b/libcxx/include/array
index 0e9af4198632d1..839adafa5cfa09 100644
--- a/libcxx/include/array
+++ b/libcxx/include/array
@@ -120,6 +120,7 @@ template <size_t I, class T, size_t N> const T&& get(const array<T, N>&&) noexce
 #include <__config>
 #include <__fwd/array.h>
 #include <__iterator/reverse_iterator.h>
+#include <__iterator/static_bounded_iter.h>
 #include <__iterator/wrap_iter.h>
 #include <__tuple/sfinae_helpers.h>
 #include <__type_traits/conditional.h>
@@ -177,7 +178,10 @@ struct _LIBCPP_TEMPLATE_VIS array {
   using const_reference = const value_type&;
   using pointer         = value_type*;
   using const_pointer   = const value_type*;
-#if defined(_LIBCPP_ABI_USE_WRAP_ITER_IN_STD_ARRAY)
+#if defined(_LIBCPP_ABI_BOUNDED_ITERATORS_IN_STD_ARRAY)
+  using iterator       = __static_bounded_iter<pointer, _Size>;
+  using const_iterator = __static_bounded_iter<const_pointer, _Size>;
+#elif defined(_LIBCPP_ABI_USE_WRAP_ITER_IN_STD_ARRAY)
   using iterator       = __wrap_iter<pointer>;
   using const_iterator = __wrap_iter<const_pointer>;
 #else
@@ -201,13 +205,33 @@ struct _LIBCPP_TEMPLATE_VIS array {
   }
 
   // iterators:
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 iterator begin() _NOEXCEPT { return iterator(data()); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 iterator begin() _NOEXCEPT {
+#if defined(_LIBCPP_ABI_BOUNDED_ITERATORS_IN_STD_ARRAY)
+    return std::__make_static_bounded_iter<_Size>(data(), data());
+#else
+    return iterator(data());
+#endif
+  }
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 const_iterator begin() const _NOEXCEPT {
+#if defined(_LIBCPP_ABI_BOUNDED_ITERATORS_IN_STD_ARRAY)
+    return std::__make_static_bounded_iter<_Size>(data(), data());
+#else
     return const_iterator(data());
+#endif
+  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 iterator end() _NOEXCEPT {
+#if defined(_LIBCPP_ABI_BOUNDED_ITERATORS_IN_STD_ARRAY)
+    return std::__make_static_bounded_iter<_Size>(data() + _Size, data());
+#else
+    return iterator(data() + _Size);
+#endif
   }
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 iterator end() _NOEXCEPT { return iterator(data() + _Size); }
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 const_iterator end() const _NOEXCEPT {
+#if defined(_LIBCPP_ABI_BOUNDED_ITERATORS_IN_STD_ARRAY)
+    return std::__make_static_bounded_iter<_Size>(data() + _Size, data());
+#else
     return const_iterator(data() + _Size);
+#endif
   }
 
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 reverse_iterator rbegin() _NOEXCEPT {
@@ -277,7 +301,10 @@ struct _LIBCPP_TEMPLATE_VIS array<_Tp, 0> {
   using const_reference = const value_type&;
   using pointer         = value_type*;
   using const_pointer   = const value_type*;
-#if defined(_LIBCPP_ABI_USE_WRAP_ITER_IN_STD_ARRAY)
+#if defined(_LIBCPP_ABI_BOUNDED_ITERATORS_IN_STD_ARRAY)
+  using iterator       = __static_bounded_iter<pointer, 0>;
+  using const_iterator = __static_bounded_iter<const_pointer, 0>;
+#elif defined(_LIBCPP_ABI_USE_WRAP_ITER_IN_STD_ARRAY)
   using iterator       = __wrap_iter<pointer>;
   using const_iterator = __wrap_iter<const_pointer>;
 #else
@@ -309,13 +336,33 @@ struct _LIBCPP_TEMPLATE_VIS array<_Tp, 0> {
   }
 
   // iterators:
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 iterator begin() _NOEXCEPT { return iterator(data()); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 iterator begin() _NOEXCEPT {
+#if defined(_LIBCPP_ABI_BOUNDED_ITERATORS_IN_STD_ARRAY)
+    return std::__make_static_bounded_iter<0>(data(), data());
+#else
+    return iterator(data());
+#endif
+  }
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 const_iterator begin() const _NOEXCEPT {
+#if defined(_LIBCPP_ABI_BOUNDED_ITERATORS_IN_STD_ARRAY)
+    return std::__make_static_bounded_iter<0>(data(), data());
+#else
     return const_iterator(data());
+#endif
+  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 iterator end() _NOEXCEPT {
+#if defined(_LIBCPP_ABI_BOUNDED_ITERATORS_IN_STD_ARRAY)
+    return std::__make_static_bounded_iter<0>(data(), data());
+#else
+    return iterator(data());
+#endif
   }
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 iterator end() _NOEXCEPT { return iterator(data()); }
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 const_iterator end() const _NOEXCEPT {
+#if defined(_LIBCPP_ABI_BOUNDED_ITERATORS_IN_STD_ARRAY)
+    return std::__make_static_bounded_iter<0>(data(), data());
+#else
     return const_iterator(data());
+#endif
   }
 
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 reverse_iterator rbegin() _NOEXCEPT {
diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap
index 881c0ca6c3669c..9576a9c677cf89 100644
--- a/libcxx/include/module.modulemap
+++ b/libcxx/include/module.modulemap
@@ -1417,6 +1417,7 @@ module std [system] {
     module segmented_iterator         { header "__iterator/segmented_iterator.h" }
     module size                       { header "__iterator/size.h" }
     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 wrap_iter                  { header "__iterator/wrap_iter.h" }
 
diff --git a/libcxx/test/libcxx/c...
[truncated]

@ldionne
Copy link
Member Author

ldionne commented Oct 1, 2024

CC @davidben

Copy link

github-actions bot commented Oct 1, 2024

✅ With the latest revision this PR passed the C/C++ code formatter.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
hardening Issues related to the hardening effort libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[libc++] Use a __bounded_iter flavor in std::array
3 participants