[libcxx-commits] [libcxx] [libc++] Add support for bounded iterators in std::array (PR #110729)
Louis Dionne via libcxx-commits
libcxx-commits at lists.llvm.org
Mon Oct 21 13:10:57 PDT 2024
https://github.com/ldionne updated https://github.com/llvm/llvm-project/pull/110729
>From afb4e0b2ae45c0cbc618c72ea83a16170d61a54f Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Tue, 1 Oct 2024 15:06:49 -0400
Subject: [PATCH 1/8] [libc++] Add support for bounded iterators in std::array
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
---
...-hardening-mode-fast-with-abi-breaks.cmake | 9 +-
libcxx/include/CMakeLists.txt | 1 +
libcxx/include/__configuration/abi.h | 6 +
libcxx/include/__iterator/bounded_iter.h | 4 +-
.../include/__iterator/static_bounded_iter.h | 268 ++++++++++++++++++
libcxx/include/array | 59 +++-
libcxx/include/module.modulemap | 1 +
.../array.zero/assert.subscript.pass.cpp | 41 ---
.../sequences/array}/assert.back.pass.cpp | 0
.../sequences/array}/assert.front.pass.cpp | 2 +-
.../sequences/array/assert.indexing.pass.cpp | 79 ++++++
.../sequences/array/assert.iterators.pass.cpp | 145 ++++++++++
libcxx/utils/libcxx/test/features.py | 1 +
13 files changed, 565 insertions(+), 51 deletions(-)
create mode 100644 libcxx/include/__iterator/static_bounded_iter.h
delete mode 100644 libcxx/test/libcxx/containers/sequences/array/array.zero/assert.subscript.pass.cpp
rename libcxx/test/{libcxx/containers/sequences/array/array.zero => std/containers/sequences/array}/assert.back.pass.cpp (100%)
rename libcxx/test/{libcxx/containers/sequences/array/array.zero => std/containers/sequences/array}/assert.front.pass.cpp (95%)
create mode 100644 libcxx/test/std/containers/sequences/array/assert.indexing.pass.cpp
create mode 100644 libcxx/test/std/containers/sequences/array/assert.iterators.pass.cpp
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 a107314518b1fa..5a485e1b18dca5 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 06e93d2452904d..9e11ebd2ff6965 100644
--- a/libcxx/include/module.modulemap
+++ b/libcxx/include/module.modulemap
@@ -1422,6 +1422,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/containers/sequences/array/array.zero/assert.subscript.pass.cpp b/libcxx/test/libcxx/containers/sequences/array/array.zero/assert.subscript.pass.cpp
deleted file mode 100644
index ab65ce223f7c54..00000000000000
--- a/libcxx/test/libcxx/containers/sequences/array/array.zero/assert.subscript.pass.cpp
+++ /dev/null
@@ -1,41 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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: has-unix-headers
-// UNSUPPORTED: c++03
-// UNSUPPORTED: libcpp-hardening-mode=none
-// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
-
-// test that array<T, 0>::operator[] triggers an assertion
-
-#include <array>
-
-#include "check_assertion.h"
-
-int main(int, char**) {
- {
- typedef std::array<int, 0> C;
- C c = {};
- C const& cc = c;
- TEST_LIBCPP_ASSERT_FAILURE(c[0], "cannot call array<T, 0>::operator[] on a zero-sized array");
- TEST_LIBCPP_ASSERT_FAILURE(c[1], "cannot call array<T, 0>::operator[] on a zero-sized array");
- TEST_LIBCPP_ASSERT_FAILURE(cc[0], "cannot call array<T, 0>::operator[] on a zero-sized array");
- TEST_LIBCPP_ASSERT_FAILURE(cc[1], "cannot call array<T, 0>::operator[] on a zero-sized array");
- }
- {
- typedef std::array<const int, 0> C;
- C c = {{}};
- C const& cc = c;
- TEST_LIBCPP_ASSERT_FAILURE(c[0], "cannot call array<T, 0>::operator[] on a zero-sized array");
- TEST_LIBCPP_ASSERT_FAILURE(c[1], "cannot call array<T, 0>::operator[] on a zero-sized array");
- TEST_LIBCPP_ASSERT_FAILURE(cc[0], "cannot call array<T, 0>::operator[] on a zero-sized array");
- TEST_LIBCPP_ASSERT_FAILURE(cc[1], "cannot call array<T, 0>::operator[] on a zero-sized array");
- }
-
- return 0;
-}
diff --git a/libcxx/test/libcxx/containers/sequences/array/array.zero/assert.back.pass.cpp b/libcxx/test/std/containers/sequences/array/assert.back.pass.cpp
similarity index 100%
rename from libcxx/test/libcxx/containers/sequences/array/array.zero/assert.back.pass.cpp
rename to libcxx/test/std/containers/sequences/array/assert.back.pass.cpp
diff --git a/libcxx/test/libcxx/containers/sequences/array/array.zero/assert.front.pass.cpp b/libcxx/test/std/containers/sequences/array/assert.front.pass.cpp
similarity index 95%
rename from libcxx/test/libcxx/containers/sequences/array/array.zero/assert.front.pass.cpp
rename to libcxx/test/std/containers/sequences/array/assert.front.pass.cpp
index 1ea0035ee4cc50..7dbbd6f0abc07a 100644
--- a/libcxx/test/libcxx/containers/sequences/array/array.zero/assert.front.pass.cpp
+++ b/libcxx/test/std/containers/sequences/array/assert.front.pass.cpp
@@ -11,7 +11,7 @@
// UNSUPPORTED: libcpp-hardening-mode=none
// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
-// test that array<T, 0>::back() triggers an assertion
+// test that array<T, 0>::front() triggers an assertion
#include <array>
diff --git a/libcxx/test/std/containers/sequences/array/assert.indexing.pass.cpp b/libcxx/test/std/containers/sequences/array/assert.indexing.pass.cpp
new file mode 100644
index 00000000000000..ea0ffce3b3007d
--- /dev/null
+++ b/libcxx/test/std/containers/sequences/array/assert.indexing.pass.cpp
@@ -0,0 +1,79 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: has-unix-headers
+// UNSUPPORTED: c++03
+// UNSUPPORTED: libcpp-hardening-mode=none
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+// <array>
+
+// Test that operator[] triggers an assertion when accessing the array out-of-bounds.
+
+#include <array>
+
+#include "check_assertion.h"
+
+int main(int, char**) {
+ // Check with an empty array
+ {
+ {
+ using Array = std::array<int, 0>;
+ Array c = {};
+ Array const& cc = c;
+ TEST_LIBCPP_ASSERT_FAILURE(c[0], "cannot call array<T, 0>::operator[] on a zero-sized array");
+ TEST_LIBCPP_ASSERT_FAILURE(c[1], "cannot call array<T, 0>::operator[] on a zero-sized array");
+ TEST_LIBCPP_ASSERT_FAILURE(cc[0], "cannot call array<T, 0>::operator[] on a zero-sized array");
+ TEST_LIBCPP_ASSERT_FAILURE(cc[1], "cannot call array<T, 0>::operator[] on a zero-sized array");
+ }
+ {
+ using Array = std::array<const int, 0>;
+ Array c = {{}};
+ Array const& cc = c;
+ TEST_LIBCPP_ASSERT_FAILURE(c[0], "cannot call array<T, 0>::operator[] on a zero-sized array");
+ TEST_LIBCPP_ASSERT_FAILURE(c[1], "cannot call array<T, 0>::operator[] on a zero-sized array");
+ TEST_LIBCPP_ASSERT_FAILURE(cc[0], "cannot call array<T, 0>::operator[] on a zero-sized array");
+ TEST_LIBCPP_ASSERT_FAILURE(cc[1], "cannot call array<T, 0>::operator[] on a zero-sized array");
+ }
+ }
+
+ // Check with non-empty arrays
+ {
+ {
+ using Array = std::array<int, 1>;
+ Array c = {};
+ Array const& cc = c;
+ TEST_LIBCPP_ASSERT_FAILURE(c[2], "out-of-bounds access in std::array<T, N>");
+ TEST_LIBCPP_ASSERT_FAILURE(cc[2], "out-of-bounds access in std::array<T, N>");
+ }
+ {
+ using Array = std::array<const int, 1>;
+ Array c = {{}};
+ Array const& cc = c;
+ TEST_LIBCPP_ASSERT_FAILURE(c[2], "out-of-bounds access in std::array<T, N>");
+ TEST_LIBCPP_ASSERT_FAILURE(cc[2], "out-of-bounds access in std::array<T, N>");
+ }
+
+ {
+ using Array = std::array<int, 5>;
+ Array c = {};
+ Array const& cc = c;
+ TEST_LIBCPP_ASSERT_FAILURE(c[99], "out-of-bounds access in std::array<T, N>");
+ TEST_LIBCPP_ASSERT_FAILURE(cc[99], "out-of-bounds access in std::array<T, N>");
+ }
+ {
+ using Array = std::array<const int, 5>;
+ Array c = {{}};
+ Array const& cc = c;
+ TEST_LIBCPP_ASSERT_FAILURE(c[99], "out-of-bounds access in std::array<T, N>");
+ TEST_LIBCPP_ASSERT_FAILURE(cc[99], "out-of-bounds access in std::array<T, N>");
+ }
+ }
+
+ return 0;
+}
diff --git a/libcxx/test/std/containers/sequences/array/assert.iterators.pass.cpp b/libcxx/test/std/containers/sequences/array/assert.iterators.pass.cpp
new file mode 100644
index 00000000000000..21a763e71e18a5
--- /dev/null
+++ b/libcxx/test/std/containers/sequences/array/assert.iterators.pass.cpp
@@ -0,0 +1,145 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: has-unix-headers, libcpp-has-abi-bounded-iterators-in-std-array
+// UNSUPPORTED: c++03
+// UNSUPPORTED: libcpp-hardening-mode=none
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+// <array>
+
+// Make sure that std::array's iterators check for OOB accesses when the right hardening settings
+// are enabled.
+
+#include <array>
+#include <cstddef>
+#include <iterator>
+
+#include "check_assertion.h"
+
+template <typename Iter>
+void test_iterator(Iter begin, Iter end) {
+ std::ptrdiff_t distance = std::distance(begin, end);
+
+ // Dereferencing an iterator at the end.
+ {
+ TEST_LIBCPP_ASSERT_FAILURE(*end, "__static_bounded_iter::operator*: Attempt to dereference an iterator at the end");
+ TEST_LIBCPP_ASSERT_FAILURE(
+ end.operator->(), "__static_bounded_iter::operator->: Attempt to dereference an iterator at the end");
+ }
+
+ // Incrementing an iterator past the end.
+ {
+ auto it = end;
+ TEST_LIBCPP_ASSERT_FAILURE(it++, "__static_bounded_iter::operator++: Attempt to advance an iterator past the end");
+ it = end;
+ TEST_LIBCPP_ASSERT_FAILURE(++it, "__static_bounded_iter::operator++: Attempt to advance an iterator past the end");
+ }
+
+ // Decrementing an iterator past the start.
+ {
+ auto it = begin;
+ TEST_LIBCPP_ASSERT_FAILURE(it--, "__static_bounded_iter::operator--: Attempt to rewind an iterator past the start");
+ it = begin;
+ TEST_LIBCPP_ASSERT_FAILURE(--it, "__static_bounded_iter::operator--: Attempt to rewind an iterator past the start");
+ }
+
+ // Advancing past the end with operator+= and operator+.
+ {
+ [[maybe_unused]] const char* msg = "__static_bounded_iter::operator+=: Attempt to advance an iterator past the end";
+ auto it = end;
+ TEST_LIBCPP_ASSERT_FAILURE(it += 1, msg);
+ TEST_LIBCPP_ASSERT_FAILURE(end + 1, msg);
+ it = begin;
+ TEST_LIBCPP_ASSERT_FAILURE(it += (distance + 1), msg);
+ TEST_LIBCPP_ASSERT_FAILURE(begin + (distance + 1), msg);
+ }
+
+ // Advancing past the end with operator-= and operator-.
+ {
+ [[maybe_unused]] const char* msg = "__static_bounded_iter::operator-=: Attempt to advance an iterator past the end";
+ auto it = end;
+ TEST_LIBCPP_ASSERT_FAILURE(it -= (-1), msg);
+ TEST_LIBCPP_ASSERT_FAILURE(end - (-1), msg);
+ it = begin;
+ TEST_LIBCPP_ASSERT_FAILURE(it -= (-distance - 1), msg);
+ TEST_LIBCPP_ASSERT_FAILURE(begin - (-distance - 1), msg);
+ }
+
+ // Rewinding past the start with operator+= and operator+.
+ {
+ [[maybe_unused]] const char* msg =
+ "__static_bounded_iter::operator+=: Attempt to rewind an iterator past the start";
+ auto it = begin;
+ TEST_LIBCPP_ASSERT_FAILURE(it += (-1), msg);
+ TEST_LIBCPP_ASSERT_FAILURE(begin + (-1), msg);
+ it = end;
+ TEST_LIBCPP_ASSERT_FAILURE(it += (-distance - 1), msg);
+ TEST_LIBCPP_ASSERT_FAILURE(end + (-distance - 1), msg);
+ }
+
+ // Rewinding past the start with operator-= and operator-.
+ {
+ [[maybe_unused]] const char* msg =
+ "__static_bounded_iter::operator-=: Attempt to rewind an iterator past the start";
+ auto it = begin;
+ TEST_LIBCPP_ASSERT_FAILURE(it -= 1, msg);
+ TEST_LIBCPP_ASSERT_FAILURE(begin - 1, msg);
+ it = end;
+ TEST_LIBCPP_ASSERT_FAILURE(it -= (distance + 1), msg);
+ TEST_LIBCPP_ASSERT_FAILURE(end - (distance + 1), msg);
+ }
+
+ // Out-of-bounds operator[].
+ {
+ [[maybe_unused]] const char* end_msg =
+ "__static_bounded_iter::operator[]: Attempt to index an iterator at or past the end";
+ [[maybe_unused]] const char* past_end_msg =
+ "__static_bounded_iter::operator[]: Attempt to index an iterator at or past the end";
+ [[maybe_unused]] const char* past_start_msg =
+ "__static_bounded_iter::operator[]: Attempt to index an iterator past the start";
+ TEST_LIBCPP_ASSERT_FAILURE(begin[distance], end_msg);
+ TEST_LIBCPP_ASSERT_FAILURE(begin[distance + 1], past_end_msg);
+ TEST_LIBCPP_ASSERT_FAILURE(begin[-1], past_start_msg);
+ TEST_LIBCPP_ASSERT_FAILURE(begin[-99], past_start_msg);
+
+ if (distance > 0) {
+ auto it = begin + 1;
+ TEST_LIBCPP_ASSERT_FAILURE(it[distance - 1], end_msg);
+ TEST_LIBCPP_ASSERT_FAILURE(it[distance], past_end_msg);
+ TEST_LIBCPP_ASSERT_FAILURE(it[-2], past_start_msg);
+ TEST_LIBCPP_ASSERT_FAILURE(it[-99], past_start_msg);
+ }
+ }
+}
+
+int main(int, char**) {
+ // Empty array
+ {
+ std::array<int, 0> array = {};
+
+ // array::iterator
+ test_iterator(array.begin(), array.end());
+
+ // array::const_iterator
+ test_iterator(array.cbegin(), array.cend());
+ }
+
+ // Non-empty array
+ {
+ std::array<int, 10> array = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+
+ // array::iterator
+ test_iterator(array.begin(), array.end());
+
+ // array::const_iterator
+ test_iterator(array.cbegin(), array.cend());
+ }
+
+ return 0;
+}
diff --git a/libcxx/utils/libcxx/test/features.py b/libcxx/utils/libcxx/test/features.py
index 735eb5ac949dc0..bdb0993e897ea0 100644
--- a/libcxx/utils/libcxx/test/features.py
+++ b/libcxx/utils/libcxx/test/features.py
@@ -374,6 +374,7 @@ def _mingwSupportsModules(cfg):
"_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_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",
"_LIBCPP_ABI_FIX_UNORDERED_CONTAINER_SIZE_TYPE": "libcpp-has-abi-fix-unordered-container-size-type",
"_LIBCPP_DEPRECATED_ABI_DISABLE_PAIR_TRIVIAL_COPY_CTOR": "libcpp-deprecated-abi-disable-pair-trivial-copy-ctor",
>From 9d1a86944ba9dc7ed89bbcba6225cebd3ae7f213 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Tue, 1 Oct 2024 15:57:18 -0400
Subject: [PATCH 2/8] Formatting
---
.../test/std/containers/sequences/array/assert.back.pass.cpp | 4 ++--
.../test/std/containers/sequences/array/assert.front.pass.cpp | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/libcxx/test/std/containers/sequences/array/assert.back.pass.cpp b/libcxx/test/std/containers/sequences/array/assert.back.pass.cpp
index c20b0c9a804f26..b9dec01033334a 100644
--- a/libcxx/test/std/containers/sequences/array/assert.back.pass.cpp
+++ b/libcxx/test/std/containers/sequences/array/assert.back.pass.cpp
@@ -20,14 +20,14 @@
int main(int, char**) {
{
typedef std::array<int, 0> C;
- C c = {};
+ C c = {};
C const& cc = c;
TEST_LIBCPP_ASSERT_FAILURE(c.back(), "cannot call array<T, 0>::back() on a zero-sized array");
TEST_LIBCPP_ASSERT_FAILURE(cc.back(), "cannot call array<T, 0>::back() on a zero-sized array");
}
{
typedef std::array<const int, 0> C;
- C c = {{}};
+ C c = {{}};
C const& cc = c;
TEST_LIBCPP_ASSERT_FAILURE(c.back(), "cannot call array<T, 0>::back() on a zero-sized array");
TEST_LIBCPP_ASSERT_FAILURE(cc.back(), "cannot call array<T, 0>::back() on a zero-sized array");
diff --git a/libcxx/test/std/containers/sequences/array/assert.front.pass.cpp b/libcxx/test/std/containers/sequences/array/assert.front.pass.cpp
index 7dbbd6f0abc07a..67a70a1d63c521 100644
--- a/libcxx/test/std/containers/sequences/array/assert.front.pass.cpp
+++ b/libcxx/test/std/containers/sequences/array/assert.front.pass.cpp
@@ -20,14 +20,14 @@
int main(int, char**) {
{
typedef std::array<int, 0> C;
- C c = {};
+ C c = {};
C const& cc = c;
TEST_LIBCPP_ASSERT_FAILURE(c.front(), "cannot call array<T, 0>::front() on a zero-sized array");
TEST_LIBCPP_ASSERT_FAILURE(cc.front(), "cannot call array<T, 0>::front() on a zero-sized array");
}
{
typedef std::array<const int, 0> C;
- C c = {{}};
+ C c = {{}};
C const& cc = c;
TEST_LIBCPP_ASSERT_FAILURE(c.front(), "cannot call array<T, 0>::front() on a zero-sized array");
TEST_LIBCPP_ASSERT_FAILURE(cc.front(), "cannot call array<T, 0>::front() on a zero-sized array");
>From 86ac68af82683291abd2c3caba6856a9d600f05e Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Mon, 21 Oct 2024 13:18:32 -0400
Subject: [PATCH 3/8] Add release note and update docs
---
libcxx/docs/Hardening.rst | 6 ++++++
libcxx/docs/ReleaseNotes/20.rst | 3 +++
libcxx/include/__configuration/abi.h | 2 +-
3 files changed, 10 insertions(+), 1 deletion(-)
diff --git a/libcxx/docs/Hardening.rst b/libcxx/docs/Hardening.rst
index 67791a5e55ac7c..b61bcc6fbdc982 100644
--- a/libcxx/docs/Hardening.rst
+++ b/libcxx/docs/Hardening.rst
@@ -341,6 +341,12 @@ Vendors can use the following ABI options to enable additional hardening checks:
ABI impact: changes the iterator type of ``vector`` (except ``vector<bool>``).
+- ``_LIBCPP_ABI_BOUNDED_ITERATORS_IN_STD_ARRAY`` -- changes the iterator type of ``std::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 ``std::array``, its size and its layout.
+
ABI tags
--------
diff --git a/libcxx/docs/ReleaseNotes/20.rst b/libcxx/docs/ReleaseNotes/20.rst
index 44912d2ddafac6..aaac6e47d4757f 100644
--- a/libcxx/docs/ReleaseNotes/20.rst
+++ b/libcxx/docs/ReleaseNotes/20.rst
@@ -60,6 +60,9 @@ Improvements and New Features
compile times and smaller debug information as well as better code generation if optimizations are disabled.
The Chromium project measured a 5% reduction in object file and debug information size.
+- The ``_LIBCPP_ABI_BOUNDED_ITERATORS_IN_STD_ARRAY`` ABI configuration was added, which allows storing valid bounds
+ in ``std::array::iterator`` and detecting OOB accesses when the appropriate hardening mode is enabled.
+
Deprecations and Removals
-------------------------
diff --git a/libcxx/include/__configuration/abi.h b/libcxx/include/__configuration/abi.h
index 17623ca491c185..8c6b46bfa559b9 100644
--- a/libcxx/include/__configuration/abi.h
+++ b/libcxx/include/__configuration/abi.h
@@ -179,7 +179,7 @@
// 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`.
+// ABI impact: changes the iterator type of `array`, its size and its layout.
// #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.
>From f864509371971b935bef5fc447db123258b09aae Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Mon, 21 Oct 2024 13:19:31 -0400
Subject: [PATCH 4/8] Address review comment about redundant opening comment
---
libcxx/include/__iterator/static_bounded_iter.h | 3 ---
1 file changed, 3 deletions(-)
diff --git a/libcxx/include/__iterator/static_bounded_iter.h b/libcxx/include/__iterator/static_bounded_iter.h
index c322014447099f..9a1990ada3c983 100644
--- a/libcxx/include/__iterator/static_bounded_iter.h
+++ b/libcxx/include/__iterator/static_bounded_iter.h
@@ -31,9 +31,6 @@ _LIBCPP_PUSH_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
>From 03f1150069df248f79309ee48747f87adb1bc4a6 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Mon, 21 Oct 2024 13:21:37 -0400
Subject: [PATCH 5/8] NFC: spacing
---
libcxx/include/__iterator/static_bounded_iter.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libcxx/include/__iterator/static_bounded_iter.h b/libcxx/include/__iterator/static_bounded_iter.h
index 9a1990ada3c983..92e879ccefcb0c 100644
--- a/libcxx/include/__iterator/static_bounded_iter.h
+++ b/libcxx/include/__iterator/static_bounded_iter.h
@@ -37,7 +37,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD
// 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> >
+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;
>From c5d94b81def19ad9706ce8ee85ccc350fe6c3bee Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Mon, 21 Oct 2024 13:45:46 -0400
Subject: [PATCH 6/8] Optimize 0-sized array iterators
---
.../include/__iterator/static_bounded_iter.h | 101 ++++++++++++------
1 file changed, 67 insertions(+), 34 deletions(-)
diff --git a/libcxx/include/__iterator/static_bounded_iter.h b/libcxx/include/__iterator/static_bounded_iter.h
index 92e879ccefcb0c..fd6bbe71f9c325 100644
--- a/libcxx/include/__iterator/static_bounded_iter.h
+++ b/libcxx/include/__iterator/static_bounded_iter.h
@@ -31,6 +31,37 @@ _LIBCPP_PUSH_MACROS
_LIBCPP_BEGIN_NAMESPACE_STD
+template <class _Iterator, size_t _Size>
+struct __static_bounded_iter_storage {
+ _LIBCPP_HIDE_FROM_ABI
+ _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit __static_bounded_iter_storage(_Iterator __current, _Iterator __begin)
+ : __current_(__current), __begin_(__begin) {}
+
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Iterator& __current() _NOEXCEPT { return __current_; }
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Iterator __current() const _NOEXCEPT { return __current_; }
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Iterator __begin() const _NOEXCEPT { return __begin_; }
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Iterator __end() const _NOEXCEPT { return __begin_ + _Size; }
+
+private:
+ _Iterator __current_; // current iterator
+ _Iterator __begin_; // start of the valid range, which is [__begin_, __begin_ + _Size)
+};
+
+template <class _Iterator, size_t _Size>
+struct __static_bounded_iter_storage<_Iterator, 0> {
+ _LIBCPP_HIDE_FROM_ABI
+ _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit __static_bounded_iter_storage(_Iterator __current, _Iterator /* __begin */)
+ : __current_(__current) {}
+
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Iterator& __current() _NOEXCEPT { return __current_; }
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Iterator __current() const _NOEXCEPT { return __current_; }
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Iterator __begin() const _NOEXCEPT { return __current_; }
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Iterator __end() const _NOEXCEPT { return __current_ + _Size; }
+
+private:
+ _Iterator __current_; // current iterator
+};
+
// 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
@@ -60,8 +91,7 @@ struct __static_bounded_iter {
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_) {}
+ : __storage_(__other.__storage_.__current(), __other.__storage_.__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;
@@ -72,7 +102,7 @@ struct __static_bounded_iter {
// 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) {
+ : __storage_(__current, __begin) {
_LIBCPP_ASSERT_INTERNAL(
__begin <= __current, "__static_bounded_iter(current, begin): current and begin are inconsistent");
_LIBCPP_ASSERT_INTERNAL(
@@ -86,23 +116,24 @@ struct __static_bounded_iter {
// 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_;
+ __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_);
+ __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");
+ __n >= __begin() - __current(),
+ "__static_bounded_iter::operator[]: Attempt to index an iterator past the start");
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
- __n < __end() - __current_,
+ __n < __end() - __current(),
"__static_bounded_iter::operator[]: Attempt to index an iterator at or past the end");
- return __current_[__n];
+ return __current()[__n];
}
// Arithmetic operations.
@@ -110,8 +141,8 @@ struct __static_bounded_iter {
// 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_;
+ __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 {
@@ -122,8 +153,8 @@ struct __static_bounded_iter {
_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_;
+ __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 {
@@ -134,11 +165,11 @@ struct __static_bounded_iter {
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 __static_bounded_iter& operator+=(difference_type __n) _NOEXCEPT {
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
- __n >= __begin_ - __current_,
+ __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;
+ __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
@@ -156,11 +187,11 @@ struct __static_bounded_iter {
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 __static_bounded_iter& operator-=(difference_type __n) _NOEXCEPT {
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
- __n <= __current_ - __begin_,
+ __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;
+ __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
@@ -171,7 +202,7 @@ struct __static_bounded_iter {
}
_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_;
+ return __x.__current() - __y.__current();
}
// Comparison operations.
@@ -182,42 +213,42 @@ struct __static_bounded_iter {
// 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_;
+ 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_;
+ 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_;
+ 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_;
+ 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_;
+ 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_;
+ 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_;
+ return __x.__current() <=> __y.__current();
} else {
- if (__x.__current_ < __y.__current_)
+ if (__x.__current() < __y.__current())
return strong_ordering::less;
- if (__x.__current_ == __y.__current_)
+ if (__x.__current() == __y.__current())
return strong_ordering::equal;
return strong_ordering::greater;
@@ -230,10 +261,12 @@ struct __static_bounded_iter {
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)
+ __static_bounded_iter_storage<_Iterator, _Size> __storage_;
- _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Iterator __end() const _NOEXCEPT { return __begin_ + _Size; }
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Iterator& __current() _NOEXCEPT { return __storage_.__current(); }
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Iterator __current() const _NOEXCEPT { return __storage_.__current(); }
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Iterator __begin() const _NOEXCEPT { return __storage_.__begin(); }
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Iterator __end() const _NOEXCEPT { return __storage_.__end(); }
};
template <size_t _Size, class _It>
@@ -254,7 +287,7 @@ struct pointer_traits<__static_bounded_iter<_Iterator, _Size> > {
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_);
+ return std::__to_address(__it.__current());
}
};
>From f7e01e478c257a988b3858d548632461094c486e Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Mon, 21 Oct 2024 14:15:09 -0400
Subject: [PATCH 7/8] Fix invalid partial specialization
---
libcxx/include/__iterator/static_bounded_iter.h | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/libcxx/include/__iterator/static_bounded_iter.h b/libcxx/include/__iterator/static_bounded_iter.h
index fd6bbe71f9c325..907977b5eb235a 100644
--- a/libcxx/include/__iterator/static_bounded_iter.h
+++ b/libcxx/include/__iterator/static_bounded_iter.h
@@ -33,6 +33,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD
template <class _Iterator, size_t _Size>
struct __static_bounded_iter_storage {
+ _LIBCPP_HIDE_FROM_ABI __static_bounded_iter_storage() = default;
_LIBCPP_HIDE_FROM_ABI
_LIBCPP_CONSTEXPR_SINCE_CXX14 explicit __static_bounded_iter_storage(_Iterator __current, _Iterator __begin)
: __current_(__current), __begin_(__begin) {}
@@ -47,8 +48,9 @@ struct __static_bounded_iter_storage {
_Iterator __begin_; // start of the valid range, which is [__begin_, __begin_ + _Size)
};
-template <class _Iterator, size_t _Size>
+template <class _Iterator>
struct __static_bounded_iter_storage<_Iterator, 0> {
+ _LIBCPP_HIDE_FROM_ABI __static_bounded_iter_storage() = default;
_LIBCPP_HIDE_FROM_ABI
_LIBCPP_CONSTEXPR_SINCE_CXX14 explicit __static_bounded_iter_storage(_Iterator __current, _Iterator /* __begin */)
: __current_(__current) {}
@@ -56,7 +58,7 @@ struct __static_bounded_iter_storage<_Iterator, 0> {
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Iterator& __current() _NOEXCEPT { return __current_; }
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Iterator __current() const _NOEXCEPT { return __current_; }
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Iterator __begin() const _NOEXCEPT { return __current_; }
- _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Iterator __end() const _NOEXCEPT { return __current_ + _Size; }
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Iterator __end() const _NOEXCEPT { return __current_; }
private:
_Iterator __current_; // current iterator
>From b387eacadc62679bc52f42f4c46eb492085c0fdf Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Mon, 21 Oct 2024 16:10:43 -0400
Subject: [PATCH 8/8] Try to adjust constexpr for GCC/c++11
---
libcxx/include/__iterator/static_bounded_iter.h | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/libcxx/include/__iterator/static_bounded_iter.h b/libcxx/include/__iterator/static_bounded_iter.h
index 907977b5eb235a..b233d9d798343f 100644
--- a/libcxx/include/__iterator/static_bounded_iter.h
+++ b/libcxx/include/__iterator/static_bounded_iter.h
@@ -38,10 +38,10 @@ struct __static_bounded_iter_storage {
_LIBCPP_CONSTEXPR_SINCE_CXX14 explicit __static_bounded_iter_storage(_Iterator __current, _Iterator __begin)
: __current_(__current), __begin_(__begin) {}
- _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Iterator& __current() _NOEXCEPT { return __current_; }
- _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Iterator __current() const _NOEXCEPT { return __current_; }
- _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Iterator __begin() const _NOEXCEPT { return __begin_; }
- _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Iterator __end() const _NOEXCEPT { return __begin_ + _Size; }
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 _Iterator& __current() _NOEXCEPT { return __current_; }
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 _Iterator __current() const _NOEXCEPT { return __current_; }
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 _Iterator __begin() const _NOEXCEPT { return __begin_; }
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 _Iterator __end() const _NOEXCEPT { return __begin_ + _Size; }
private:
_Iterator __current_; // current iterator
@@ -55,10 +55,10 @@ struct __static_bounded_iter_storage<_Iterator, 0> {
_LIBCPP_CONSTEXPR_SINCE_CXX14 explicit __static_bounded_iter_storage(_Iterator __current, _Iterator /* __begin */)
: __current_(__current) {}
- _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Iterator& __current() _NOEXCEPT { return __current_; }
- _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Iterator __current() const _NOEXCEPT { return __current_; }
- _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Iterator __begin() const _NOEXCEPT { return __current_; }
- _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Iterator __end() const _NOEXCEPT { return __current_; }
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 _Iterator& __current() _NOEXCEPT { return __current_; }
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 _Iterator __current() const _NOEXCEPT { return __current_; }
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 _Iterator __begin() const _NOEXCEPT { return __current_; }
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 _Iterator __end() const _NOEXCEPT { return __current_; }
private:
_Iterator __current_; // current iterator
More information about the libcxx-commits
mailing list