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