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