xref: /llvm-project/libcxx/test/std/ranges/range.access/begin.pass.cpp (revision 12978b3e23a2766732595391c193e5631f40a3db)
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 
9 // UNSUPPORTED: c++03, c++11, c++14, c++17
10 
11 // std::ranges::begin
12 // std::ranges::cbegin
13 
14 #include <ranges>
15 
16 #include <cassert>
17 #include <utility>
18 #include "test_macros.h"
19 #include "test_iterators.h"
20 
21 using RangeBeginT = decltype(std::ranges::begin);
22 using RangeCBeginT = decltype(std::ranges::cbegin);
23 
24 static int globalBuff[8];
25 
26 static_assert(!std::is_invocable_v<RangeBeginT, int (&&)[10]>);
27 static_assert( std::is_invocable_v<RangeBeginT, int (&)[10]>);
28 static_assert(!std::is_invocable_v<RangeBeginT, int (&&)[]>);
29 static_assert( std::is_invocable_v<RangeBeginT, int (&)[]>);
30 static_assert(!std::is_invocable_v<RangeCBeginT, int (&&)[10]>);
31 static_assert( std::is_invocable_v<RangeCBeginT, int (&)[10]>);
32 static_assert(!std::is_invocable_v<RangeCBeginT, int (&&)[]>);
33 static_assert( std::is_invocable_v<RangeCBeginT, int (&)[]>);
34 
35 struct Incomplete;
36 static_assert(!std::is_invocable_v<RangeBeginT, Incomplete(&&)[]>);
37 static_assert(!std::is_invocable_v<RangeBeginT, const Incomplete(&&)[]>);
38 static_assert(!std::is_invocable_v<RangeCBeginT, Incomplete(&&)[]>);
39 static_assert(!std::is_invocable_v<RangeCBeginT, const Incomplete(&&)[]>);
40 
41 static_assert(!std::is_invocable_v<RangeBeginT, Incomplete(&&)[10]>);
42 static_assert(!std::is_invocable_v<RangeBeginT, const Incomplete(&&)[10]>);
43 static_assert(!std::is_invocable_v<RangeCBeginT, Incomplete(&&)[10]>);
44 static_assert(!std::is_invocable_v<RangeCBeginT, const Incomplete(&&)[10]>);
45 
46 // This case is IFNDR; we handle it SFINAE-friendly.
47 LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeBeginT, Incomplete(&)[]>);
48 LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeBeginT, const Incomplete(&)[]>);
49 LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeCBeginT, Incomplete(&)[]>);
50 LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeCBeginT, const Incomplete(&)[]>);
51 
52 // This case is IFNDR; we handle it SFINAE-friendly.
53 LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeBeginT, Incomplete(&)[10]>);
54 LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeBeginT, const Incomplete(&)[10]>);
55 LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeCBeginT, Incomplete(&)[10]>);
56 LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeCBeginT, const Incomplete(&)[10]>);
57 
58 struct BeginMember {
59   int x;
beginBeginMember60   constexpr const int *begin() const { return &x; }
61 };
62 
63 // Ensure that we can't call with rvalues with borrowing disabled.
64 static_assert( std::is_invocable_v<RangeBeginT, BeginMember &>);
65 static_assert(!std::is_invocable_v<RangeBeginT, BeginMember &&>);
66 static_assert( std::is_invocable_v<RangeBeginT, BeginMember const&>);
67 static_assert(!std::is_invocable_v<RangeBeginT, BeginMember const&&>);
68 static_assert( std::is_invocable_v<RangeCBeginT, BeginMember &>);
69 static_assert(!std::is_invocable_v<RangeCBeginT, BeginMember &&>);
70 static_assert( std::is_invocable_v<RangeCBeginT, BeginMember const&>);
71 static_assert(!std::is_invocable_v<RangeCBeginT, BeginMember const&&>);
72 
testReturnTypes()73 constexpr bool testReturnTypes() {
74   {
75     int *x[2];
76     ASSERT_SAME_TYPE(decltype(std::ranges::begin(x)), int**);
77     ASSERT_SAME_TYPE(decltype(std::ranges::cbegin(x)), int* const*);
78   }
79   {
80     int x[2][2];
81     ASSERT_SAME_TYPE(decltype(std::ranges::begin(x)), int(*)[2]);
82     ASSERT_SAME_TYPE(decltype(std::ranges::cbegin(x)), const int(*)[2]);
83   }
84   {
85     struct Different {
86       char*& begin();
87       short*& begin() const;
88     } x;
89     ASSERT_SAME_TYPE(decltype(std::ranges::begin(x)), char*);
90     ASSERT_SAME_TYPE(decltype(std::ranges::cbegin(x)), short*);
91   }
92   return true;
93 }
94 
testArray()95 constexpr bool testArray() {
96   int a[2];
97   assert(std::ranges::begin(a) == a);
98   assert(std::ranges::cbegin(a) == a);
99 
100   int b[2][2];
101   assert(std::ranges::begin(b) == b);
102   assert(std::ranges::cbegin(b) == b);
103 
104   BeginMember c[2];
105   assert(std::ranges::begin(c) == c);
106   assert(std::ranges::cbegin(c) == c);
107 
108   return true;
109 }
110 
111 struct BeginMemberReturnsInt {
112   int begin() const;
113 };
114 static_assert(!std::is_invocable_v<RangeBeginT, BeginMemberReturnsInt const&>);
115 
116 struct BeginMemberReturnsVoidPtr {
117   const void *begin() const;
118 };
119 static_assert(!std::is_invocable_v<RangeBeginT, BeginMemberReturnsVoidPtr const&>);
120 
121 struct EmptyBeginMember {
122   struct iterator {};
123   iterator begin() const;
124 };
125 static_assert(!std::is_invocable_v<RangeBeginT, EmptyBeginMember const&>);
126 
127 struct PtrConvertibleBeginMember {
128   struct iterator { operator int*() const; };
129   iterator begin() const;
130 };
131 static_assert(!std::is_invocable_v<RangeBeginT, PtrConvertibleBeginMember const&>);
132 
133 struct NonConstBeginMember {
134   int x;
beginNonConstBeginMember135   constexpr int *begin() { return &x; }
136 };
137 static_assert( std::is_invocable_v<RangeBeginT,  NonConstBeginMember &>);
138 static_assert(!std::is_invocable_v<RangeBeginT,  NonConstBeginMember const&>);
139 static_assert(!std::is_invocable_v<RangeCBeginT, NonConstBeginMember &>);
140 static_assert(!std::is_invocable_v<RangeCBeginT, NonConstBeginMember const&>);
141 
142 struct EnabledBorrowingBeginMember {
beginEnabledBorrowingBeginMember143   constexpr int *begin() const { return &globalBuff[0]; }
144 };
145 template<>
146 inline constexpr bool std::ranges::enable_borrowed_range<EnabledBorrowingBeginMember> = true;
147 
148 struct BeginMemberFunction {
149   int x;
beginBeginMemberFunction150   constexpr const int *begin() const { return &x; }
151   friend int *begin(BeginMemberFunction const&);
152 };
153 
154 struct EmptyPtrBeginMember {
155   struct Empty {};
156   Empty x;
beginEmptyPtrBeginMember157   constexpr const Empty *begin() const { return &x; }
158 };
159 
testBeginMember()160 constexpr bool testBeginMember() {
161   BeginMember a;
162   assert(std::ranges::begin(a) == &a.x);
163   assert(std::ranges::cbegin(a) == &a.x);
164   static_assert(!std::is_invocable_v<RangeBeginT, BeginMember&&>);
165   static_assert(!std::is_invocable_v<RangeCBeginT, BeginMember&&>);
166 
167   NonConstBeginMember b;
168   assert(std::ranges::begin(b) == &b.x);
169   static_assert(!std::is_invocable_v<RangeCBeginT, NonConstBeginMember&>);
170 
171   EnabledBorrowingBeginMember c;
172   assert(std::ranges::begin(c) == &globalBuff[0]);
173   assert(std::ranges::cbegin(c) == &globalBuff[0]);
174   assert(std::ranges::begin(std::move(c)) == &globalBuff[0]);
175   assert(std::ranges::cbegin(std::move(c)) == &globalBuff[0]);
176 
177   BeginMemberFunction d;
178   assert(std::ranges::begin(d) == &d.x);
179   assert(std::ranges::cbegin(d) == &d.x);
180 
181   EmptyPtrBeginMember e;
182   assert(std::ranges::begin(e) == &e.x);
183   assert(std::ranges::cbegin(e) == &e.x);
184 
185   return true;
186 }
187 
188 
189 struct BeginFunction {
190   int x;
begin(BeginFunction const & bf)191   friend constexpr const int *begin(BeginFunction const& bf) { return &bf.x; }
192 };
193 static_assert( std::is_invocable_v<RangeBeginT,  BeginFunction const&>);
194 static_assert(!std::is_invocable_v<RangeBeginT,  BeginFunction &&>);
195 static_assert(std::is_invocable_v<RangeBeginT, BeginFunction&>); // Ill-formed before P2602R2 Poison Pills are Too Toxic
196 static_assert( std::is_invocable_v<RangeCBeginT, BeginFunction const&>);
197 static_assert( std::is_invocable_v<RangeCBeginT, BeginFunction &>);
198 
199 struct BeginFunctionReturnsInt {
200   friend int begin(BeginFunctionReturnsInt const&);
201 };
202 static_assert(!std::is_invocable_v<RangeBeginT, BeginFunctionReturnsInt const&>);
203 
204 struct BeginFunctionReturnsVoidPtr {
205   friend void *begin(BeginFunctionReturnsVoidPtr const&);
206 };
207 static_assert(!std::is_invocable_v<RangeBeginT, BeginFunctionReturnsVoidPtr const&>);
208 
209 struct BeginFunctionReturnsPtrConvertible {
210   struct iterator { operator int*() const; };
211   friend iterator begin(BeginFunctionReturnsPtrConvertible const&);
212 };
213 static_assert(!std::is_invocable_v<RangeBeginT, BeginFunctionReturnsPtrConvertible const&>);
214 
215 struct BeginFunctionByValue {
begin(BeginFunctionByValue)216   friend constexpr int *begin(BeginFunctionByValue) { return &globalBuff[1]; }
217 };
218 static_assert(!std::is_invocable_v<RangeCBeginT, BeginFunctionByValue>);
219 
220 struct BeginFunctionEnabledBorrowing {
begin(BeginFunctionEnabledBorrowing)221   friend constexpr int *begin(BeginFunctionEnabledBorrowing) { return &globalBuff[2]; }
222 };
223 template<>
224 inline constexpr bool std::ranges::enable_borrowed_range<BeginFunctionEnabledBorrowing> = true;
225 
226 struct BeginFunctionReturnsEmptyPtr {
227   struct Empty {};
228   Empty x;
begin(BeginFunctionReturnsEmptyPtr const & bf)229   friend constexpr const Empty *begin(BeginFunctionReturnsEmptyPtr const& bf) { return &bf.x; }
230 };
231 
232 struct BeginFunctionWithDataMember {
233   int x;
234   int begin;
begin(BeginFunctionWithDataMember const & bf)235   friend constexpr const int *begin(BeginFunctionWithDataMember const& bf) { return &bf.x; }
236 };
237 
238 struct BeginFunctionWithPrivateBeginMember {
239   int y;
begin(BeginFunctionWithPrivateBeginMember const & bf)240   friend constexpr const int *begin(BeginFunctionWithPrivateBeginMember const& bf) { return &bf.y; }
241 private:
242   const int *begin() const;
243 };
244 
testBeginFunction()245 constexpr bool testBeginFunction() {
246   BeginFunction a{};
247   const BeginFunction aa{};
248   assert(std::ranges::begin(a) == &a.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic
249   assert(std::ranges::cbegin(a) == &a.x);
250   assert(std::ranges::begin(aa) == &aa.x);
251   assert(std::ranges::cbegin(aa) == &aa.x);
252 
253   BeginFunctionByValue b{};
254   const BeginFunctionByValue bb{};
255   assert(std::ranges::begin(b) == &globalBuff[1]);
256   assert(std::ranges::cbegin(b) == &globalBuff[1]);
257   assert(std::ranges::begin(bb) == &globalBuff[1]);
258   assert(std::ranges::cbegin(bb) == &globalBuff[1]);
259 
260   BeginFunctionEnabledBorrowing c{};
261   const BeginFunctionEnabledBorrowing cc{};
262   assert(std::ranges::begin(std::move(c)) == &globalBuff[2]);
263   assert(std::ranges::cbegin(std::move(c)) == &globalBuff[2]);
264   assert(std::ranges::begin(std::move(cc)) == &globalBuff[2]);
265   assert(std::ranges::cbegin(std::move(cc)) == &globalBuff[2]);
266 
267   BeginFunctionReturnsEmptyPtr d{};
268   const BeginFunctionReturnsEmptyPtr dd{};
269   assert(std::ranges::begin(d) == &d.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic
270   assert(std::ranges::cbegin(d) == &d.x);
271   assert(std::ranges::begin(dd) == &dd.x);
272   assert(std::ranges::cbegin(dd) == &dd.x);
273 
274   BeginFunctionWithDataMember e{};
275   const BeginFunctionWithDataMember ee{};
276   assert(std::ranges::begin(e) == &e.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic
277   assert(std::ranges::begin(ee) == &ee.x);
278   assert(std::ranges::cbegin(e) == &e.x);
279   assert(std::ranges::cbegin(ee) == &ee.x);
280 
281   BeginFunctionWithPrivateBeginMember f{};
282   const BeginFunctionWithPrivateBeginMember ff{};
283   assert(std::ranges::begin(f) == &f.y); // Ill-formed before P2602R2 Poison Pills are Too Toxic
284   assert(std::ranges::cbegin(f) == &f.y);
285   assert(std::ranges::begin(ff) == &ff.y);
286   assert(std::ranges::cbegin(ff) == &ff.y);
287 
288   return true;
289 }
290 
291 
292 ASSERT_NOEXCEPT(std::ranges::begin(std::declval<int (&)[10]>()));
293 ASSERT_NOEXCEPT(std::ranges::cbegin(std::declval<int (&)[10]>()));
294 
295 struct NoThrowMemberBegin {
296   ThrowingIterator<int> begin() const noexcept; // auto(t.begin()) doesn't throw
297 } ntmb;
298 static_assert(noexcept(std::ranges::begin(ntmb)));
299 static_assert(noexcept(std::ranges::cbegin(ntmb)));
300 
301 struct NoThrowADLBegin {
302   friend ThrowingIterator<int> begin(NoThrowADLBegin&) noexcept;  // auto(begin(t)) doesn't throw
303   friend ThrowingIterator<int> begin(const NoThrowADLBegin&) noexcept;
304 } ntab;
305 static_assert(noexcept(std::ranges::begin(ntab)));
306 static_assert(noexcept(std::ranges::cbegin(ntab)));
307 
308 struct NoThrowMemberBeginReturnsRef {
309   ThrowingIterator<int>& begin() const noexcept; // auto(t.begin()) may throw
310 } ntmbrr;
311 static_assert(!noexcept(std::ranges::begin(ntmbrr)));
312 static_assert(!noexcept(std::ranges::cbegin(ntmbrr)));
313 
314 struct BeginReturnsArrayRef {
315     auto begin() const noexcept -> int(&)[10];
316 } brar;
317 static_assert(noexcept(std::ranges::begin(brar)));
318 static_assert(noexcept(std::ranges::cbegin(brar)));
319 
320 // Test ADL-proofing.
321 struct Incomplete;
322 template<class T> struct Holder { T t; };
323 static_assert(!std::is_invocable_v<RangeBeginT, Holder<Incomplete>*>);
324 static_assert(!std::is_invocable_v<RangeBeginT, Holder<Incomplete>*&>);
325 static_assert(!std::is_invocable_v<RangeCBeginT, Holder<Incomplete>*>);
326 static_assert(!std::is_invocable_v<RangeCBeginT, Holder<Incomplete>*&>);
327 
main(int,char **)328 int main(int, char**) {
329   static_assert(testReturnTypes());
330 
331   testArray();
332   static_assert(testArray());
333 
334   testBeginMember();
335   static_assert(testBeginMember());
336 
337   testBeginFunction();
338   static_assert(testBeginFunction());
339 
340   return 0;
341 }
342