xref: /llvm-project/libcxx/test/libcxx/containers/views/views.span/assert.iterator-indexing.pass.cpp (revision a83f8e0314fcdda162e54cbba1c9dcf230dff093)
1 //===----------------------------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 // UNSUPPORTED: c++03, c++11, c++14, c++17
9 
10 // Make sure that std::span's iterators check for OOB accesses when the debug mode is enabled.
11 
12 // REQUIRES: has-unix-headers, libcpp-has-abi-bounded-iterators
13 // UNSUPPORTED: libcpp-hardening-mode=none
14 
15 #include <span>
16 
17 #include "check_assertion.h"
18 
19 struct Foo {
20   int x;
21 };
22 
23 template <typename Iter>
test_iterator(Iter begin,Iter end,bool reverse)24 void test_iterator(Iter begin, Iter end, bool reverse) {
25   std::ptrdiff_t distance = std::distance(begin, end);
26 
27   // Dereferencing an iterator at the end.
28   {
29     TEST_LIBCPP_ASSERT_FAILURE(
30         *end,
31         reverse ? "__bounded_iter::operator--: Attempt to rewind an iterator past the start"
32                 : "__bounded_iter::operator*: Attempt to dereference an iterator at the end");
33 #if _LIBCPP_STD_VER >= 20
34     // In C++20 mode, std::reverse_iterator implements operator->, but not operator*, with
35     // std::prev instead of operator--. std::prev ultimately calls operator+
36     TEST_LIBCPP_ASSERT_FAILURE(
37         end->x,
38         reverse ? "__bounded_iter::operator+=: Attempt to rewind an iterator past the start"
39                 : "__bounded_iter::operator->: Attempt to dereference an iterator at the end");
40 #else
41     TEST_LIBCPP_ASSERT_FAILURE(
42         end->x,
43         reverse ? "__bounded_iter::operator--: Attempt to rewind an iterator past the start"
44                 : "__bounded_iter::operator->: Attempt to dereference an iterator at the end");
45 #endif
46   }
47 
48   // Incrementing an iterator past the end.
49   {
50     [[maybe_unused]] const char* msg =
51         reverse ? "__bounded_iter::operator--: Attempt to rewind an iterator past the start"
52                 : "__bounded_iter::operator++: Attempt to advance an iterator past the end";
53     auto it = end;
54     TEST_LIBCPP_ASSERT_FAILURE(it++, msg);
55     TEST_LIBCPP_ASSERT_FAILURE(++it, msg);
56   }
57 
58   // Decrementing an iterator past the start.
59   {
60     [[maybe_unused]] const char* msg =
61         reverse ? "__bounded_iter::operator++: Attempt to advance an iterator past the end"
62                 : "__bounded_iter::operator--: Attempt to rewind an iterator past the start";
63     auto it = begin;
64     TEST_LIBCPP_ASSERT_FAILURE(it--, msg);
65     TEST_LIBCPP_ASSERT_FAILURE(--it, msg);
66   }
67 
68   // Advancing past the end with operator+= and operator+.
69   {
70     [[maybe_unused]] const char* msg =
71         reverse ? "__bounded_iter::operator-=: Attempt to rewind an iterator past the start"
72                 : "__bounded_iter::operator+=: Attempt to advance an iterator past the end";
73     auto it = end;
74     TEST_LIBCPP_ASSERT_FAILURE(it += 1, msg);
75     TEST_LIBCPP_ASSERT_FAILURE(end + 1, msg);
76     it = begin;
77     TEST_LIBCPP_ASSERT_FAILURE(it += (distance + 1), msg);
78     TEST_LIBCPP_ASSERT_FAILURE(begin + (distance + 1), msg);
79   }
80 
81   // Advancing past the end with operator-= and operator-.
82   {
83     [[maybe_unused]] const char* msg =
84         reverse ? "__bounded_iter::operator+=: Attempt to rewind an iterator past the start"
85                 : "__bounded_iter::operator-=: Attempt to advance an iterator past the end";
86     auto it = end;
87     TEST_LIBCPP_ASSERT_FAILURE(it -= (-1), msg);
88     TEST_LIBCPP_ASSERT_FAILURE(end - (-1), msg);
89     it = begin;
90     TEST_LIBCPP_ASSERT_FAILURE(it -= (-distance - 1), msg);
91     TEST_LIBCPP_ASSERT_FAILURE(begin - (-distance - 1), msg);
92   }
93 
94   // Rewinding past the start with operator+= and operator+.
95   {
96     [[maybe_unused]] const char* msg =
97         reverse ? "__bounded_iter::operator-=: Attempt to advance an iterator past the end"
98                 : "__bounded_iter::operator+=: Attempt to rewind an iterator past the start";
99     auto it = begin;
100     TEST_LIBCPP_ASSERT_FAILURE(it += (-1), msg);
101     TEST_LIBCPP_ASSERT_FAILURE(begin + (-1), msg);
102     it = end;
103     TEST_LIBCPP_ASSERT_FAILURE(it += (-distance - 1), msg);
104     TEST_LIBCPP_ASSERT_FAILURE(end + (-distance - 1), msg);
105   }
106 
107   // Rewinding past the start with operator-= and operator-.
108   {
109     [[maybe_unused]] const char* msg =
110         reverse ? "__bounded_iter::operator+=: Attempt to advance an iterator past the end"
111                 : "__bounded_iter::operator-=: Attempt to rewind an iterator past the start";
112     auto it = begin;
113     TEST_LIBCPP_ASSERT_FAILURE(it -= 1, msg);
114     TEST_LIBCPP_ASSERT_FAILURE(begin - 1, msg);
115     it = end;
116     TEST_LIBCPP_ASSERT_FAILURE(it -= (distance + 1), msg);
117     TEST_LIBCPP_ASSERT_FAILURE(end - (distance + 1), msg);
118   }
119 
120   // Out-of-bounds operator[].
121   {
122     [[maybe_unused]] const char* end_msg =
123         reverse ? "__bounded_iter::operator--: Attempt to rewind an iterator past the start"
124                 : "__bounded_iter::operator[]: Attempt to index an iterator at or past the end";
125     [[maybe_unused]] const char* past_end_msg =
126         reverse ? "__bounded_iter::operator-=: Attempt to rewind an iterator past the start"
127                 : "__bounded_iter::operator[]: Attempt to index an iterator at or past the end";
128     [[maybe_unused]] const char* past_start_msg =
129         reverse ? "__bounded_iter::operator-=: Attempt to advance an iterator past the end"
130                 : "__bounded_iter::operator[]: Attempt to index an iterator past the start";
131     TEST_LIBCPP_ASSERT_FAILURE(begin[distance], end_msg);
132     TEST_LIBCPP_ASSERT_FAILURE(begin[distance + 1], past_end_msg);
133     TEST_LIBCPP_ASSERT_FAILURE(begin[-1], past_start_msg);
134     TEST_LIBCPP_ASSERT_FAILURE(begin[-99], past_start_msg);
135 
136     auto it = begin + 1;
137     TEST_LIBCPP_ASSERT_FAILURE(it[distance - 1], end_msg);
138     TEST_LIBCPP_ASSERT_FAILURE(it[distance], past_end_msg);
139     TEST_LIBCPP_ASSERT_FAILURE(it[-2], past_start_msg);
140     TEST_LIBCPP_ASSERT_FAILURE(it[-99], past_start_msg);
141   }
142 }
143 
main(int,char **)144 int main(int, char**) {
145   // span<T>::iterator
146   {
147     Foo array[] = {{0}, {1}, {2}};
148     std::span<Foo> const span(array, 3);
149     test_iterator(span.begin(), span.end(), /*reverse=*/false);
150   }
151 
152   // span<T, N>::iterator
153   {
154     Foo array[] = {{0}, {1}, {2}};
155     std::span<Foo, 3> const span(array, 3);
156     test_iterator(span.begin(), span.end(), /*reverse=*/false);
157   }
158 
159   // span<T>::reverse_iterator
160   {
161     Foo array[] = {{0}, {1}, {2}};
162     std::span<Foo> const span(array, 3);
163     test_iterator(span.rbegin(), span.rend(), /*reverse=*/true);
164   }
165 
166   // span<T, N>::reverse_iterator
167   {
168     Foo array[] = {{0}, {1}, {2}};
169     std::span<Foo, 3> const span(array, 3);
170     test_iterator(span.rbegin(), span.rend(), /*reverse=*/true);
171   }
172 
173   return 0;
174 }
175