@@ -31,13 +31,20 @@ _LIBCPP_BEGIN_NAMESPACE_STD
31
31
// Iterator wrapper that carries the valid range it is allowed to access.
32
32
//
33
33
// This is a simple iterator wrapper for contiguous iterators that points
34
- // within a [begin, end) range and carries these bounds with it. The iterator
35
- // ensures that it is pointing within that [begin, end) range when it is
36
- // dereferenced.
34
+ // within a [begin, end] range and carries these bounds with it. The iterator
35
+ // ensures that it is pointing within [begin, end) range when it is
36
+ // dereferenced. It also ensures that it is never iterated outside of
37
+ // [begin, end]. This is important for two reasons:
37
38
//
38
- // Arithmetic operations are allowed and the bounds of the resulting iterator
39
- // are not checked. Hence, it is possible to create an iterator pointing outside
40
- // its range, but it is not possible to dereference it.
39
+ // 1. It allows `operator*` and `operator++` bounds checks to be `iter != end`.
40
+ // This is both less for the optimizer to prove, and aligns with how callers
41
+ // typically use iterators.
42
+ //
43
+ // 2. Advancing an iterator out of bounds is undefined behavior (see the table
44
+ // in [input.iterators]). In particular, when the underlying iterator is a
45
+ // pointer, it is undefined at the language level (see [expr.add]). If
46
+ // bounded iterators exhibited this undefined behavior, we risk compiler
47
+ // optimizations deleting non-redundant bounds checks.
41
48
template <class _Iterator , class = __enable_if_t < __libcpp_is_contiguous_iterator<_Iterator>::value > >
42
49
struct __bounded_iter {
43
50
using value_type = typename iterator_traits<_Iterator>::value_type;
@@ -51,8 +58,8 @@ struct __bounded_iter {
51
58
52
59
// Create a singular iterator.
53
60
//
54
- // Such an iterator does not point to any object and is conceptually out of bounds , so it is
55
- // not dereferenceable. Observing operations like comparison and assignment are valid.
61
+ // Such an iterator points past the end of an empty span , so it is not dereferenceable.
62
+ // Observing operations like comparison and assignment are valid.
56
63
_LIBCPP_HIDE_FROM_ABI __bounded_iter () = default;
57
64
58
65
_LIBCPP_HIDE_FROM_ABI __bounded_iter (__bounded_iter const &) = default;
@@ -70,18 +77,20 @@ struct __bounded_iter {
70
77
71
78
private:
72
79
// Create an iterator wrapping the given iterator, and whose bounds are described
73
- // by the provided [begin, end) range.
80
+ // by the provided [begin, end] range.
74
81
//
75
- // This constructor does not check whether the resulting iterator is within its bounds.
76
- // However, it does check that the provided [begin, end) range is a valid range (that
77
- // is, begin <= end).
82
+ // The constructor does not check whether the resulting iterator is within its bounds. It is a
83
+ // responsibility of the container to ensure that the given bounds are valid.
78
84
//
79
85
// Since it is non-standard for iterators to have this constructor, __bounded_iter must
80
86
// be created via `std::__make_bounded_iter`.
81
87
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit __bounded_iter (
82
88
_Iterator __current, _Iterator __begin, _Iterator __end)
83
89
: __current_(__current), __begin_(__begin), __end_(__end) {
84
- _LIBCPP_ASSERT_INTERNAL (__begin <= __end, " __bounded_iter(current, begin, end): [begin, end) is not a valid range" );
90
+ _LIBCPP_ASSERT_INTERNAL (
91
+ __begin <= __current, " __bounded_iter(current, begin, end): current and begin are inconsistent" );
92
+ _LIBCPP_ASSERT_INTERNAL (
93
+ __current <= __end, " __bounded_iter(current, begin, end): current and end are inconsistent" );
85
94
}
86
95
87
96
template <class _It >
@@ -90,30 +99,37 @@ struct __bounded_iter {
90
99
public:
91
100
// Dereference and indexing operations.
92
101
//
93
- // These operations check that the iterator is dereferenceable, that is within [begin, end).
102
+ // These operations check that the iterator is dereferenceable. Since the class invariant is
103
+ // that the iterator is always within `[begin, end]`, we only need to check it's not pointing to
104
+ // `end`. This is easier for the optimizer because it aligns with the `iter != container.end()`
105
+ // checks that typical callers already use (see
106
+ // https://github.com/llvm/llvm-project/issues/78829).
94
107
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 reference operator *() const _NOEXCEPT {
95
108
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS (
96
- __in_bounds ( __current_) , " __bounded_iter::operator*: Attempt to dereference an out-of-range iterator" );
109
+ __current_ != __end_ , " __bounded_iter::operator*: Attempt to dereference an iterator at the end " );
97
110
return *__current_;
98
111
}
99
112
100
113
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 pointer operator ->() const _NOEXCEPT {
101
114
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS (
102
- __in_bounds ( __current_) , " __bounded_iter::operator->: Attempt to dereference an out-of-range iterator" );
115
+ __current_ != __end_ , " __bounded_iter::operator->: Attempt to dereference an iterator at the end " );
103
116
return std::__to_address (__current_);
104
117
}
105
118
106
119
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 reference operator [](difference_type __n) const _NOEXCEPT {
107
120
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS (
108
- __in_bounds (__current_ + __n), " __bounded_iter::operator[]: Attempt to index an iterator out-of-range" );
121
+ __n >= __begin_ - __current_, " __bounded_iter::operator[]: Attempt to index an iterator past the start" );
122
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS (
123
+ __n < __end_ - __current_, " __bounded_iter::operator[]: Attempt to index an iterator at or past the end" );
109
124
return __current_[__n];
110
125
}
111
126
112
127
// Arithmetic operations.
113
128
//
114
- // These operations do not check that the resulting iterator is within the bounds, since that
115
- // would make it impossible to create a past-the-end iterator.
129
+ // These operations check that the iterator remains within `[begin, end]`.
116
130
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 __bounded_iter& operator ++() _NOEXCEPT {
131
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS (
132
+ __current_ != __end_, " __bounded_iter::operator++: Attempt to advance an iterator past the end" );
117
133
++__current_;
118
134
return *this ;
119
135
}
@@ -124,6 +140,8 @@ struct __bounded_iter {
124
140
}
125
141
126
142
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 __bounded_iter& operator --() _NOEXCEPT {
143
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS (
144
+ __current_ != __begin_, " __bounded_iter::operator--: Attempt to rewind an iterator past the start" );
127
145
--__current_;
128
146
return *this ;
129
147
}
@@ -134,6 +152,10 @@ struct __bounded_iter {
134
152
}
135
153
136
154
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 __bounded_iter& operator +=(difference_type __n) _NOEXCEPT {
155
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS (
156
+ __n >= __begin_ - __current_, " __bounded_iter::operator+=: Attempt to rewind an iterator past the start" );
157
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS (
158
+ __n <= __end_ - __current_, " __bounded_iter::operator+=: Attempt to advance an iterator past the end" );
137
159
__current_ += __n;
138
160
return *this ;
139
161
}
@@ -151,6 +173,10 @@ struct __bounded_iter {
151
173
}
152
174
153
175
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 __bounded_iter& operator -=(difference_type __n) _NOEXCEPT {
176
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS (
177
+ __n <= __current_ - __begin_, " __bounded_iter::operator-=: Attempt to rewind an iterator past the start" );
178
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS (
179
+ __n >= __current_ - __end_, " __bounded_iter::operator-=: Attempt to advance an iterator past the end" );
154
180
__current_ -= __n;
155
181
return *this ;
156
182
}
@@ -197,15 +223,10 @@ struct __bounded_iter {
197
223
}
198
224
199
225
private:
200
- // Return whether the given iterator is in the bounds of this __bounded_iter.
201
- _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR bool __in_bounds (_Iterator const & __iter) const {
202
- return __iter >= __begin_ && __iter < __end_;
203
- }
204
-
205
226
template <class >
206
227
friend struct pointer_traits ;
207
228
_Iterator __current_; // current iterator
208
- _Iterator __begin_, __end_; // valid range represented as [begin, end)
229
+ _Iterator __begin_, __end_; // valid range represented as [begin, end]
209
230
};
210
231
211
232
template <class _It >
0 commit comments