• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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::rbegin
12 // std::ranges::crbegin
13 
14 #include <ranges>
15 
16 #include <cassert>
17 #include <utility>
18 #include "test_macros.h"
19 #include "test_iterators.h"
20 
21 using RangeRBeginT = decltype(std::ranges::rbegin);
22 using RangeCRBeginT = decltype(std::ranges::crbegin);
23 
24 static int globalBuff[8];
25 
26 static_assert(!std::is_invocable_v<RangeRBeginT, int (&&)[10]>);
27 static_assert( std::is_invocable_v<RangeRBeginT, int (&)[10]>);
28 static_assert(!std::is_invocable_v<RangeRBeginT, int (&&)[]>);
29 static_assert(!std::is_invocable_v<RangeRBeginT, int (&)[]>);
30 static_assert(!std::is_invocable_v<RangeCRBeginT, int (&&)[10]>);
31 static_assert( std::is_invocable_v<RangeCRBeginT, int (&)[10]>);
32 static_assert(!std::is_invocable_v<RangeCRBeginT, int (&&)[]>);
33 static_assert(!std::is_invocable_v<RangeCRBeginT, int (&)[]>);
34 
35 struct Incomplete;
36 
37 static_assert(!std::is_invocable_v<RangeRBeginT, Incomplete(&&)[]>);
38 static_assert(!std::is_invocable_v<RangeRBeginT, const Incomplete(&&)[]>);
39 static_assert(!std::is_invocable_v<RangeCRBeginT, Incomplete(&&)[]>);
40 static_assert(!std::is_invocable_v<RangeCRBeginT, const Incomplete(&&)[]>);
41 
42 static_assert(!std::is_invocable_v<RangeRBeginT, Incomplete(&&)[10]>);
43 static_assert(!std::is_invocable_v<RangeRBeginT, const Incomplete(&&)[10]>);
44 static_assert(!std::is_invocable_v<RangeCRBeginT, Incomplete(&&)[10]>);
45 static_assert(!std::is_invocable_v<RangeCRBeginT, const Incomplete(&&)[10]>);
46 
47 // This case is IFNDR; we handle it SFINAE-friendly.
48 LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeRBeginT, Incomplete(&)[]>);
49 LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeRBeginT, const Incomplete(&)[]>);
50 LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeCRBeginT, Incomplete(&)[]>);
51 LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeCRBeginT, const Incomplete(&)[]>);
52 
53 // This case is IFNDR; we handle it SFINAE-friendly.
54 LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeRBeginT, Incomplete(&)[10]>);
55 LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeRBeginT, const Incomplete(&)[10]>);
56 LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeCRBeginT, Incomplete(&)[10]>);
57 LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeCRBeginT, const Incomplete(&)[10]>);
58 
59 struct RBeginMember {
60   int x;
rbeginRBeginMember61   constexpr const int *rbegin() const { return &x; }
62 };
63 
64 // Ensure that we can't call with rvalues with borrowing disabled.
65 static_assert( std::is_invocable_v<RangeRBeginT, RBeginMember &>);
66 static_assert(!std::is_invocable_v<RangeRBeginT, RBeginMember &&>);
67 static_assert( std::is_invocable_v<RangeRBeginT, RBeginMember const&>);
68 static_assert(!std::is_invocable_v<RangeRBeginT, RBeginMember const&&>);
69 static_assert( std::is_invocable_v<RangeCRBeginT, RBeginMember &>);
70 static_assert(!std::is_invocable_v<RangeCRBeginT, RBeginMember &&>);
71 static_assert( std::is_invocable_v<RangeCRBeginT, RBeginMember const&>);
72 static_assert(!std::is_invocable_v<RangeCRBeginT, RBeginMember const&&>);
73 
testReturnTypes()74 constexpr bool testReturnTypes() {
75   {
76     int *x[2];
77     ASSERT_SAME_TYPE(decltype(std::ranges::rbegin(x)), std::reverse_iterator<int**>);
78     ASSERT_SAME_TYPE(decltype(std::ranges::crbegin(x)), std::reverse_iterator<int* const*>);
79   }
80   {
81     int x[2][2];
82     ASSERT_SAME_TYPE(decltype(std::ranges::rbegin(x)), std::reverse_iterator<int(*)[2]>);
83     ASSERT_SAME_TYPE(decltype(std::ranges::crbegin(x)), std::reverse_iterator<const int(*)[2]>);
84   }
85   {
86     struct Different {
87       char*& rbegin();
88       short*& rbegin() const;
89     } x;
90     ASSERT_SAME_TYPE(decltype(std::ranges::rbegin(x)), char*);
91     ASSERT_SAME_TYPE(decltype(std::ranges::crbegin(x)), short*);
92   }
93   return true;
94 }
95 
testArray()96 constexpr bool testArray() {
97   int a[2];
98   assert(std::ranges::rbegin(a).base() == a + 2);
99   assert(std::ranges::crbegin(a).base() == a + 2);
100 
101   int b[2][2];
102   assert(std::ranges::rbegin(b).base() == b + 2);
103   assert(std::ranges::crbegin(b).base() == b + 2);
104 
105   RBeginMember c[2];
106   assert(std::ranges::rbegin(c).base() == c + 2);
107   assert(std::ranges::crbegin(c).base() == c + 2);
108 
109   return true;
110 }
111 
112 struct RBeginMemberReturnsInt {
113   int rbegin() const;
114 };
115 static_assert(!std::is_invocable_v<RangeRBeginT, RBeginMemberReturnsInt const&>);
116 
117 struct RBeginMemberReturnsVoidPtr {
118   const void *rbegin() const;
119 };
120 static_assert(!std::is_invocable_v<RangeRBeginT, RBeginMemberReturnsVoidPtr const&>);
121 
122 struct PtrConvertibleRBeginMember {
123   struct iterator { operator int*() const; };
124   iterator rbegin() const;
125 };
126 static_assert(!std::is_invocable_v<RangeRBeginT, PtrConvertibleRBeginMember const&>);
127 
128 struct NonConstRBeginMember {
129   int x;
rbeginNonConstRBeginMember130   constexpr int* rbegin() { return &x; }
131 };
132 static_assert( std::is_invocable_v<RangeRBeginT,  NonConstRBeginMember &>);
133 static_assert(!std::is_invocable_v<RangeRBeginT,  NonConstRBeginMember const&>);
134 static_assert(!std::is_invocable_v<RangeCRBeginT, NonConstRBeginMember &>);
135 static_assert(!std::is_invocable_v<RangeCRBeginT, NonConstRBeginMember const&>);
136 
137 struct EnabledBorrowingRBeginMember {
rbeginEnabledBorrowingRBeginMember138   constexpr int *rbegin() const { return globalBuff; }
139 };
140 template<>
141 inline constexpr bool std::ranges::enable_borrowed_range<EnabledBorrowingRBeginMember> = true;
142 
143 struct RBeginMemberFunction {
144   int x;
rbeginRBeginMemberFunction145   constexpr const int *rbegin() const { return &x; }
146   friend int* rbegin(RBeginMemberFunction const&);
147 };
148 
149 struct EmptyPtrRBeginMember {
150   struct Empty {};
151   Empty x;
rbeginEmptyPtrRBeginMember152   constexpr const Empty* rbegin() const { return &x; }
153 };
154 
testRBeginMember()155 constexpr bool testRBeginMember() {
156   RBeginMember a;
157   assert(std::ranges::rbegin(a) == &a.x);
158   assert(std::ranges::crbegin(a) == &a.x);
159   static_assert(!std::is_invocable_v<RangeRBeginT, RBeginMember&&>);
160   static_assert(!std::is_invocable_v<RangeCRBeginT, RBeginMember&&>);
161 
162   NonConstRBeginMember b;
163   assert(std::ranges::rbegin(b) == &b.x);
164   static_assert(!std::is_invocable_v<RangeCRBeginT, NonConstRBeginMember&>);
165 
166   EnabledBorrowingRBeginMember c;
167   assert(std::ranges::rbegin(c) == globalBuff);
168   assert(std::ranges::crbegin(c) == globalBuff);
169   assert(std::ranges::rbegin(std::move(c)) == globalBuff);
170   assert(std::ranges::crbegin(std::move(c)) == globalBuff);
171 
172   RBeginMemberFunction d;
173   assert(std::ranges::rbegin(d) == &d.x);
174   assert(std::ranges::crbegin(d) == &d.x);
175 
176   EmptyPtrRBeginMember e;
177   assert(std::ranges::rbegin(e) == &e.x);
178   assert(std::ranges::crbegin(e) == &e.x);
179 
180   return true;
181 }
182 
183 
184 struct RBeginFunction {
185   int x;
rbegin(RBeginFunction const & bf)186   friend constexpr const int* rbegin(RBeginFunction const& bf) { return &bf.x; }
187 };
188 static_assert( std::is_invocable_v<RangeRBeginT,  RBeginFunction const&>);
189 static_assert(!std::is_invocable_v<RangeRBeginT,  RBeginFunction &&>);
190 static_assert(!std::is_invocable_v<RangeRBeginT,  RBeginFunction &>);
191 static_assert( std::is_invocable_v<RangeCRBeginT, RBeginFunction const&>);
192 static_assert( std::is_invocable_v<RangeCRBeginT, RBeginFunction &>);
193 
194 struct RBeginFunctionReturnsInt {
195   friend int rbegin(RBeginFunctionReturnsInt const&);
196 };
197 static_assert(!std::is_invocable_v<RangeRBeginT, RBeginFunctionReturnsInt const&>);
198 
199 struct RBeginFunctionReturnsVoidPtr {
200   friend void *rbegin(RBeginFunctionReturnsVoidPtr const&);
201 };
202 static_assert(!std::is_invocable_v<RangeRBeginT, RBeginFunctionReturnsVoidPtr const&>);
203 
204 struct RBeginFunctionReturnsEmpty {
205   struct Empty {};
206   friend Empty rbegin(RBeginFunctionReturnsEmpty const&);
207 };
208 static_assert(!std::is_invocable_v<RangeRBeginT, RBeginFunctionReturnsEmpty const&>);
209 
210 struct RBeginFunctionReturnsPtrConvertible {
211   struct iterator { operator int*() const; };
212   friend iterator rbegin(RBeginFunctionReturnsPtrConvertible const&);
213 };
214 static_assert(!std::is_invocable_v<RangeRBeginT, RBeginFunctionReturnsPtrConvertible const&>);
215 
216 struct RBeginFunctionByValue {
rbegin(RBeginFunctionByValue)217   friend constexpr int *rbegin(RBeginFunctionByValue) { return globalBuff + 1; }
218 };
219 static_assert(!std::is_invocable_v<RangeCRBeginT, RBeginFunctionByValue>);
220 
221 struct RBeginFunctionEnabledBorrowing {
rbegin(RBeginFunctionEnabledBorrowing)222   friend constexpr int *rbegin(RBeginFunctionEnabledBorrowing) { return globalBuff + 2; }
223 };
224 template<>
225 inline constexpr bool std::ranges::enable_borrowed_range<RBeginFunctionEnabledBorrowing> = true;
226 
227 struct RBeginFunctionReturnsEmptyPtr {
228   struct Empty {};
229   Empty x;
rbegin(RBeginFunctionReturnsEmptyPtr const & bf)230   friend constexpr const Empty *rbegin(RBeginFunctionReturnsEmptyPtr const& bf) { return &bf.x; }
231 };
232 
233 struct RBeginFunctionWithDataMember {
234   int x;
235   int rbegin;
rbegin(RBeginFunctionWithDataMember const & bf)236   friend constexpr const int *rbegin(RBeginFunctionWithDataMember const& bf) { return &bf.x; }
237 };
238 
239 struct RBeginFunctionWithPrivateBeginMember {
240   int y;
rbegin(RBeginFunctionWithPrivateBeginMember const & bf)241   friend constexpr const int *rbegin(RBeginFunctionWithPrivateBeginMember const& bf) { return &bf.y; }
242 private:
243   const int *rbegin() const;
244 };
245 
testRBeginFunction()246 constexpr bool testRBeginFunction() {
247   RBeginFunction a{};
248   const RBeginFunction aa{};
249   static_assert(!std::invocable<RangeRBeginT, decltype((a))>);
250   assert(std::ranges::crbegin(a) == &a.x);
251   assert(std::ranges::rbegin(aa) == &aa.x);
252   assert(std::ranges::crbegin(aa) == &aa.x);
253 
254   RBeginFunctionByValue b{};
255   const RBeginFunctionByValue bb{};
256   assert(std::ranges::rbegin(b) == globalBuff + 1);
257   assert(std::ranges::crbegin(b) == globalBuff + 1);
258   assert(std::ranges::rbegin(bb) == globalBuff + 1);
259   assert(std::ranges::crbegin(bb) == globalBuff + 1);
260 
261   RBeginFunctionEnabledBorrowing c{};
262   const RBeginFunctionEnabledBorrowing cc{};
263   assert(std::ranges::rbegin(std::move(c)) == globalBuff + 2);
264   assert(std::ranges::crbegin(std::move(c)) == globalBuff + 2);
265   assert(std::ranges::rbegin(std::move(cc)) == globalBuff + 2);
266   assert(std::ranges::crbegin(std::move(cc)) == globalBuff + 2);
267 
268   RBeginFunctionReturnsEmptyPtr d{};
269   const RBeginFunctionReturnsEmptyPtr dd{};
270   static_assert(!std::invocable<RangeRBeginT, decltype((d))>);
271   assert(std::ranges::crbegin(d) == &d.x);
272   assert(std::ranges::rbegin(dd) == &dd.x);
273   assert(std::ranges::crbegin(dd) == &dd.x);
274 
275   RBeginFunctionWithDataMember e{};
276   const RBeginFunctionWithDataMember ee{};
277   static_assert(!std::invocable<RangeRBeginT, decltype((e))>);
278   assert(std::ranges::rbegin(ee) == &ee.x);
279   assert(std::ranges::crbegin(e) == &e.x);
280   assert(std::ranges::crbegin(ee) == &ee.x);
281 
282   RBeginFunctionWithPrivateBeginMember f{};
283   const RBeginFunctionWithPrivateBeginMember ff{};
284   static_assert(!std::invocable<RangeRBeginT, decltype((f))>);
285   assert(std::ranges::crbegin(f) == &f.y);
286   assert(std::ranges::rbegin(ff) == &ff.y);
287   assert(std::ranges::crbegin(ff) == &ff.y);
288 
289   return true;
290 }
291 
292 
293 struct MemberBeginEnd {
294   int b, e;
295   char cb, ce;
beginMemberBeginEnd296   constexpr bidirectional_iterator<int*> begin() { return bidirectional_iterator<int*>(&b); }
endMemberBeginEnd297   constexpr bidirectional_iterator<int*> end() { return bidirectional_iterator<int*>(&e); }
beginMemberBeginEnd298   constexpr bidirectional_iterator<const char*> begin() const { return bidirectional_iterator<const char*>(&cb); }
endMemberBeginEnd299   constexpr bidirectional_iterator<const char*> end() const { return bidirectional_iterator<const char*>(&ce); }
300 };
301 static_assert( std::is_invocable_v<RangeRBeginT, MemberBeginEnd&>);
302 static_assert( std::is_invocable_v<RangeRBeginT, MemberBeginEnd const&>);
303 static_assert( std::is_invocable_v<RangeCRBeginT, MemberBeginEnd const&>);
304 
305 struct FunctionBeginEnd {
306   int b, e;
307   char cb, ce;
begin(FunctionBeginEnd & v)308   friend constexpr bidirectional_iterator<int*> begin(FunctionBeginEnd& v) {
309     return bidirectional_iterator<int*>(&v.b);
310   }
end(FunctionBeginEnd & v)311   friend constexpr bidirectional_iterator<int*> end(FunctionBeginEnd& v) { return bidirectional_iterator<int*>(&v.e); }
begin(const FunctionBeginEnd & v)312   friend constexpr bidirectional_iterator<const char*> begin(const FunctionBeginEnd& v) {
313     return bidirectional_iterator<const char*>(&v.cb);
314   }
end(const FunctionBeginEnd & v)315   friend constexpr bidirectional_iterator<const char*> end(const FunctionBeginEnd& v) {
316     return bidirectional_iterator<const char*>(&v.ce);
317   }
318 };
319 static_assert( std::is_invocable_v<RangeRBeginT, FunctionBeginEnd&>);
320 static_assert( std::is_invocable_v<RangeRBeginT, FunctionBeginEnd const&>);
321 static_assert( std::is_invocable_v<RangeCRBeginT, FunctionBeginEnd const&>);
322 
323 struct MemberBeginFunctionEnd {
324   int b, e;
325   char cb, ce;
beginMemberBeginFunctionEnd326   constexpr bidirectional_iterator<int*> begin() { return bidirectional_iterator<int*>(&b); }
end(MemberBeginFunctionEnd & v)327   friend constexpr bidirectional_iterator<int*> end(MemberBeginFunctionEnd& v) {
328     return bidirectional_iterator<int*>(&v.e);
329   }
beginMemberBeginFunctionEnd330   constexpr bidirectional_iterator<const char*> begin() const { return bidirectional_iterator<const char*>(&cb); }
end(const MemberBeginFunctionEnd & v)331   friend constexpr bidirectional_iterator<const char*> end(const MemberBeginFunctionEnd& v) {
332     return bidirectional_iterator<const char*>(&v.ce);
333   }
334 };
335 static_assert( std::is_invocable_v<RangeRBeginT, MemberBeginFunctionEnd&>);
336 static_assert( std::is_invocable_v<RangeRBeginT, MemberBeginFunctionEnd const&>);
337 static_assert( std::is_invocable_v<RangeCRBeginT, MemberBeginFunctionEnd const&>);
338 
339 struct FunctionBeginMemberEnd {
340   int b, e;
341   char cb, ce;
begin(FunctionBeginMemberEnd & v)342   friend constexpr bidirectional_iterator<int*> begin(FunctionBeginMemberEnd& v) {
343     return bidirectional_iterator<int*>(&v.b);
344   }
endFunctionBeginMemberEnd345   constexpr bidirectional_iterator<int*> end() { return bidirectional_iterator<int*>(&e); }
begin(const FunctionBeginMemberEnd & v)346   friend constexpr bidirectional_iterator<const char*> begin(const FunctionBeginMemberEnd& v) {
347     return bidirectional_iterator<const char*>(&v.cb);
348   }
endFunctionBeginMemberEnd349   constexpr bidirectional_iterator<const char*> end() const { return bidirectional_iterator<const char*>(&ce); }
350 };
351 static_assert( std::is_invocable_v<RangeRBeginT, FunctionBeginMemberEnd&>);
352 static_assert( std::is_invocable_v<RangeRBeginT, FunctionBeginMemberEnd const&>);
353 static_assert( std::is_invocable_v<RangeCRBeginT, FunctionBeginMemberEnd const&>);
354 
355 struct MemberBeginEndDifferentTypes {
356   bidirectional_iterator<int*> begin();
357   bidirectional_iterator<const int*> end();
358 };
359 static_assert(!std::is_invocable_v<RangeRBeginT, MemberBeginEndDifferentTypes&>);
360 static_assert(!std::is_invocable_v<RangeCRBeginT, MemberBeginEndDifferentTypes&>);
361 
362 struct FunctionBeginEndDifferentTypes {
363   friend bidirectional_iterator<int*> begin(FunctionBeginEndDifferentTypes&);
364   friend bidirectional_iterator<const int*> end(FunctionBeginEndDifferentTypes&);
365 };
366 static_assert(!std::is_invocable_v<RangeRBeginT, FunctionBeginEndDifferentTypes&>);
367 static_assert(!std::is_invocable_v<RangeCRBeginT, FunctionBeginEndDifferentTypes&>);
368 
369 struct MemberBeginEndForwardIterators {
370   forward_iterator<int*> begin();
371   forward_iterator<int*> end();
372 };
373 static_assert(!std::is_invocable_v<RangeRBeginT, MemberBeginEndForwardIterators&>);
374 static_assert(!std::is_invocable_v<RangeCRBeginT, MemberBeginEndForwardIterators&>);
375 
376 struct FunctionBeginEndForwardIterators {
377   friend forward_iterator<int*> begin(FunctionBeginEndForwardIterators&);
378   friend forward_iterator<int*> end(FunctionBeginEndForwardIterators&);
379 };
380 static_assert(!std::is_invocable_v<RangeRBeginT, FunctionBeginEndForwardIterators&>);
381 static_assert(!std::is_invocable_v<RangeCRBeginT, FunctionBeginEndForwardIterators&>);
382 
383 struct MemberBeginOnly {
384   bidirectional_iterator<int*> begin() const;
385 };
386 static_assert(!std::is_invocable_v<RangeRBeginT, MemberBeginOnly&>);
387 static_assert(!std::is_invocable_v<RangeCRBeginT, MemberBeginOnly&>);
388 
389 struct FunctionBeginOnly {
390   friend bidirectional_iterator<int*> begin(FunctionBeginOnly&);
391 };
392 static_assert(!std::is_invocable_v<RangeRBeginT, FunctionBeginOnly&>);
393 static_assert(!std::is_invocable_v<RangeCRBeginT, FunctionBeginOnly&>);
394 
395 struct MemberEndOnly {
396   bidirectional_iterator<int*> end() const;
397 };
398 static_assert(!std::is_invocable_v<RangeRBeginT, MemberEndOnly&>);
399 static_assert(!std::is_invocable_v<RangeCRBeginT, MemberEndOnly&>);
400 
401 struct FunctionEndOnly {
402   friend bidirectional_iterator<int*> end(FunctionEndOnly&);
403 };
404 static_assert(!std::is_invocable_v<RangeRBeginT, FunctionEndOnly&>);
405 static_assert(!std::is_invocable_v<RangeCRBeginT, FunctionEndOnly&>);
406 
407 // Make sure there is no clash between the following cases:
408 // - the case that handles classes defining member `rbegin` and `rend` functions;
409 // - the case that handles classes defining `begin` and `end` functions returning reversible iterators.
410 struct MemberBeginAndRBegin {
411   int* begin() const;
412   int* end() const;
413   int* rbegin() const;
414   int* rend() const;
415 };
416 static_assert( std::is_invocable_v<RangeRBeginT, MemberBeginAndRBegin&>);
417 static_assert( std::is_invocable_v<RangeCRBeginT, MemberBeginAndRBegin&>);
418 static_assert( std::same_as<std::invoke_result_t<RangeRBeginT, MemberBeginAndRBegin&>, int*>);
419 static_assert( std::same_as<std::invoke_result_t<RangeCRBeginT, MemberBeginAndRBegin&>, int*>);
420 
testBeginEnd()421 constexpr bool testBeginEnd() {
422   MemberBeginEnd a{};
423   const MemberBeginEnd aa{};
424   assert(base(std::ranges::rbegin(a).base()) == &a.e);
425   assert(base(std::ranges::crbegin(a).base()) == &a.ce);
426   assert(base(std::ranges::rbegin(aa).base()) == &aa.ce);
427   assert(base(std::ranges::crbegin(aa).base()) == &aa.ce);
428 
429   FunctionBeginEnd b{};
430   const FunctionBeginEnd bb{};
431   assert(base(std::ranges::rbegin(b).base()) == &b.e);
432   assert(base(std::ranges::crbegin(b).base()) == &b.ce);
433   assert(base(std::ranges::rbegin(bb).base()) == &bb.ce);
434   assert(base(std::ranges::crbegin(bb).base()) == &bb.ce);
435 
436   MemberBeginFunctionEnd c{};
437   const MemberBeginFunctionEnd cc{};
438   assert(base(std::ranges::rbegin(c).base()) == &c.e);
439   assert(base(std::ranges::crbegin(c).base()) == &c.ce);
440   assert(base(std::ranges::rbegin(cc).base()) == &cc.ce);
441   assert(base(std::ranges::crbegin(cc).base()) == &cc.ce);
442 
443   FunctionBeginMemberEnd d{};
444   const FunctionBeginMemberEnd dd{};
445   assert(base(std::ranges::rbegin(d).base()) == &d.e);
446   assert(base(std::ranges::crbegin(d).base()) == &d.ce);
447   assert(base(std::ranges::rbegin(dd).base()) == &dd.ce);
448   assert(base(std::ranges::crbegin(dd).base()) == &dd.ce);
449 
450   return true;
451 }
452 
453 
454 ASSERT_NOEXCEPT(std::ranges::rbegin(std::declval<int (&)[10]>()));
455 ASSERT_NOEXCEPT(std::ranges::crbegin(std::declval<int (&)[10]>()));
456 
457 struct NoThrowMemberRBegin {
458   ThrowingIterator<int> rbegin() const noexcept; // auto(t.rbegin()) doesn't throw
459 } ntmb;
460 static_assert(noexcept(std::ranges::rbegin(ntmb)));
461 static_assert(noexcept(std::ranges::crbegin(ntmb)));
462 
463 struct NoThrowADLRBegin {
464   friend ThrowingIterator<int> rbegin(NoThrowADLRBegin&) noexcept;  // auto(rbegin(t)) doesn't throw
465   friend ThrowingIterator<int> rbegin(const NoThrowADLRBegin&) noexcept;
466 } ntab;
467 static_assert(noexcept(std::ranges::rbegin(ntab)));
468 static_assert(noexcept(std::ranges::crbegin(ntab)));
469 
470 struct NoThrowMemberRBeginReturnsRef {
471   ThrowingIterator<int>& rbegin() const noexcept; // auto(t.rbegin()) may throw
472 } ntmbrr;
473 static_assert(!noexcept(std::ranges::rbegin(ntmbrr)));
474 static_assert(!noexcept(std::ranges::crbegin(ntmbrr)));
475 
476 struct RBeginReturnsArrayRef {
477     auto rbegin() const noexcept -> int(&)[10];
478 } brar;
479 static_assert(noexcept(std::ranges::rbegin(brar)));
480 static_assert(noexcept(std::ranges::crbegin(brar)));
481 
482 struct NoThrowBeginThrowingEnd {
483   int* begin() const noexcept;
484   int* end() const;
485 } ntbte;
486 static_assert(!noexcept(std::ranges::rbegin(ntbte)));
487 static_assert(!noexcept(std::ranges::crbegin(ntbte)));
488 
489 struct NoThrowEndThrowingBegin {
490   int* begin() const;
491   int* end() const noexcept;
492 } ntetb;
493 static_assert(noexcept(std::ranges::rbegin(ntetb)));
494 static_assert(noexcept(std::ranges::crbegin(ntetb)));
495 
496 // Test ADL-proofing.
497 struct Incomplete;
498 template<class T> struct Holder { T t; };
499 static_assert(!std::is_invocable_v<RangeRBeginT, Holder<Incomplete>*>);
500 static_assert(!std::is_invocable_v<RangeRBeginT, Holder<Incomplete>*&>);
501 static_assert(!std::is_invocable_v<RangeCRBeginT, Holder<Incomplete>*>);
502 static_assert(!std::is_invocable_v<RangeCRBeginT, Holder<Incomplete>*&>);
503 
main(int,char **)504 int main(int, char**) {
505   static_assert(testReturnTypes());
506 
507   testArray();
508   static_assert(testArray());
509 
510   testRBeginMember();
511   static_assert(testRBeginMember());
512 
513   testRBeginFunction();
514   static_assert(testRBeginFunction());
515 
516   testBeginEnd();
517   static_assert(testBeginEnd());
518 
519   return 0;
520 }
521