xref: /llvm-project/libcxx/test/std/ranges/range.access/rbegin.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::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(
191     std::is_invocable_v<RangeRBeginT, RBeginFunction&>); // Ill-formed before P2602R2 Poison Pills are Too Toxic
192 static_assert( std::is_invocable_v<RangeCRBeginT, RBeginFunction const&>);
193 static_assert( std::is_invocable_v<RangeCRBeginT, RBeginFunction &>);
194 
195 struct RBeginFunctionReturnsInt {
196   friend int rbegin(RBeginFunctionReturnsInt const&);
197 };
198 static_assert(!std::is_invocable_v<RangeRBeginT, RBeginFunctionReturnsInt const&>);
199 
200 struct RBeginFunctionReturnsVoidPtr {
201   friend void *rbegin(RBeginFunctionReturnsVoidPtr const&);
202 };
203 static_assert(!std::is_invocable_v<RangeRBeginT, RBeginFunctionReturnsVoidPtr const&>);
204 
205 struct RBeginFunctionReturnsEmpty {
206   struct Empty {};
207   friend Empty rbegin(RBeginFunctionReturnsEmpty const&);
208 };
209 static_assert(!std::is_invocable_v<RangeRBeginT, RBeginFunctionReturnsEmpty const&>);
210 
211 struct RBeginFunctionReturnsPtrConvertible {
212   struct iterator { operator int*() const; };
213   friend iterator rbegin(RBeginFunctionReturnsPtrConvertible const&);
214 };
215 static_assert(!std::is_invocable_v<RangeRBeginT, RBeginFunctionReturnsPtrConvertible const&>);
216 
217 struct RBeginFunctionByValue {
rbegin(RBeginFunctionByValue)218   friend constexpr int *rbegin(RBeginFunctionByValue) { return globalBuff + 1; }
219 };
220 static_assert(!std::is_invocable_v<RangeCRBeginT, RBeginFunctionByValue>);
221 
222 struct RBeginFunctionEnabledBorrowing {
rbegin(RBeginFunctionEnabledBorrowing)223   friend constexpr int *rbegin(RBeginFunctionEnabledBorrowing) { return globalBuff + 2; }
224 };
225 template<>
226 inline constexpr bool std::ranges::enable_borrowed_range<RBeginFunctionEnabledBorrowing> = true;
227 
228 struct RBeginFunctionReturnsEmptyPtr {
229   struct Empty {};
230   Empty x;
rbegin(RBeginFunctionReturnsEmptyPtr const & bf)231   friend constexpr const Empty *rbegin(RBeginFunctionReturnsEmptyPtr const& bf) { return &bf.x; }
232 };
233 
234 struct RBeginFunctionWithDataMember {
235   int x;
236   int rbegin;
rbegin(RBeginFunctionWithDataMember const & bf)237   friend constexpr const int *rbegin(RBeginFunctionWithDataMember const& bf) { return &bf.x; }
238 };
239 
240 struct RBeginFunctionWithPrivateBeginMember {
241   int y;
rbegin(RBeginFunctionWithPrivateBeginMember const & bf)242   friend constexpr const int *rbegin(RBeginFunctionWithPrivateBeginMember const& bf) { return &bf.y; }
243 private:
244   const int *rbegin() const;
245 };
246 
testRBeginFunction()247 constexpr bool testRBeginFunction() {
248   RBeginFunction a{};
249   const RBeginFunction aa{};
250   assert(std::ranges::rbegin(a) == &a.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic
251   assert(std::ranges::crbegin(a) == &a.x);
252   assert(std::ranges::rbegin(aa) == &aa.x);
253   assert(std::ranges::crbegin(aa) == &aa.x);
254 
255   RBeginFunctionByValue b{};
256   const RBeginFunctionByValue bb{};
257   assert(std::ranges::rbegin(b) == globalBuff + 1);
258   assert(std::ranges::crbegin(b) == globalBuff + 1);
259   assert(std::ranges::rbegin(bb) == globalBuff + 1);
260   assert(std::ranges::crbegin(bb) == globalBuff + 1);
261 
262   RBeginFunctionEnabledBorrowing c{};
263   const RBeginFunctionEnabledBorrowing cc{};
264   assert(std::ranges::rbegin(std::move(c)) == globalBuff + 2);
265   assert(std::ranges::crbegin(std::move(c)) == globalBuff + 2);
266   assert(std::ranges::rbegin(std::move(cc)) == globalBuff + 2);
267   assert(std::ranges::crbegin(std::move(cc)) == globalBuff + 2);
268 
269   RBeginFunctionReturnsEmptyPtr d{};
270   const RBeginFunctionReturnsEmptyPtr dd{};
271   assert(std::ranges::rbegin(d) == &d.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic
272   assert(std::ranges::crbegin(d) == &d.x);
273   assert(std::ranges::rbegin(dd) == &dd.x);
274   assert(std::ranges::crbegin(dd) == &dd.x);
275 
276   RBeginFunctionWithDataMember e{};
277   const RBeginFunctionWithDataMember ee{};
278   assert(std::ranges::rbegin(e) == &e.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic
279   assert(std::ranges::rbegin(ee) == &ee.x);
280   assert(std::ranges::crbegin(e) == &e.x);
281   assert(std::ranges::crbegin(ee) == &ee.x);
282 
283   RBeginFunctionWithPrivateBeginMember f{};
284   const RBeginFunctionWithPrivateBeginMember ff{};
285   assert(std::ranges::rbegin(f) == &f.y); // Ill-formed before P2602R2 Poison Pills are Too Toxic
286   assert(std::ranges::crbegin(f) == &f.y);
287   assert(std::ranges::rbegin(ff) == &ff.y);
288   assert(std::ranges::crbegin(ff) == &ff.y);
289 
290   return true;
291 }
292 
293 
294 struct MemberBeginEnd {
295   int b, e;
296   char cb, ce;
beginMemberBeginEnd297   constexpr bidirectional_iterator<int*> begin() { return bidirectional_iterator<int*>(&b); }
endMemberBeginEnd298   constexpr bidirectional_iterator<int*> end() { return bidirectional_iterator<int*>(&e); }
beginMemberBeginEnd299   constexpr bidirectional_iterator<const char*> begin() const { return bidirectional_iterator<const char*>(&cb); }
endMemberBeginEnd300   constexpr bidirectional_iterator<const char*> end() const { return bidirectional_iterator<const char*>(&ce); }
301 };
302 static_assert( std::is_invocable_v<RangeRBeginT, MemberBeginEnd&>);
303 static_assert( std::is_invocable_v<RangeRBeginT, MemberBeginEnd const&>);
304 static_assert( std::is_invocable_v<RangeCRBeginT, MemberBeginEnd const&>);
305 
306 struct FunctionBeginEnd {
307   int b, e;
308   char cb, ce;
begin(FunctionBeginEnd & v)309   friend constexpr bidirectional_iterator<int*> begin(FunctionBeginEnd& v) {
310     return bidirectional_iterator<int*>(&v.b);
311   }
end(FunctionBeginEnd & v)312   friend constexpr bidirectional_iterator<int*> end(FunctionBeginEnd& v) { return bidirectional_iterator<int*>(&v.e); }
begin(const FunctionBeginEnd & v)313   friend constexpr bidirectional_iterator<const char*> begin(const FunctionBeginEnd& v) {
314     return bidirectional_iterator<const char*>(&v.cb);
315   }
end(const FunctionBeginEnd & v)316   friend constexpr bidirectional_iterator<const char*> end(const FunctionBeginEnd& v) {
317     return bidirectional_iterator<const char*>(&v.ce);
318   }
319 };
320 static_assert( std::is_invocable_v<RangeRBeginT, FunctionBeginEnd&>);
321 static_assert( std::is_invocable_v<RangeRBeginT, FunctionBeginEnd const&>);
322 static_assert( std::is_invocable_v<RangeCRBeginT, FunctionBeginEnd const&>);
323 
324 struct MemberBeginFunctionEnd {
325   int b, e;
326   char cb, ce;
beginMemberBeginFunctionEnd327   constexpr bidirectional_iterator<int*> begin() { return bidirectional_iterator<int*>(&b); }
end(MemberBeginFunctionEnd & v)328   friend constexpr bidirectional_iterator<int*> end(MemberBeginFunctionEnd& v) {
329     return bidirectional_iterator<int*>(&v.e);
330   }
beginMemberBeginFunctionEnd331   constexpr bidirectional_iterator<const char*> begin() const { return bidirectional_iterator<const char*>(&cb); }
end(const MemberBeginFunctionEnd & v)332   friend constexpr bidirectional_iterator<const char*> end(const MemberBeginFunctionEnd& v) {
333     return bidirectional_iterator<const char*>(&v.ce);
334   }
335 };
336 static_assert( std::is_invocable_v<RangeRBeginT, MemberBeginFunctionEnd&>);
337 static_assert( std::is_invocable_v<RangeRBeginT, MemberBeginFunctionEnd const&>);
338 static_assert( std::is_invocable_v<RangeCRBeginT, MemberBeginFunctionEnd const&>);
339 
340 struct FunctionBeginMemberEnd {
341   int b, e;
342   char cb, ce;
begin(FunctionBeginMemberEnd & v)343   friend constexpr bidirectional_iterator<int*> begin(FunctionBeginMemberEnd& v) {
344     return bidirectional_iterator<int*>(&v.b);
345   }
endFunctionBeginMemberEnd346   constexpr bidirectional_iterator<int*> end() { return bidirectional_iterator<int*>(&e); }
begin(const FunctionBeginMemberEnd & v)347   friend constexpr bidirectional_iterator<const char*> begin(const FunctionBeginMemberEnd& v) {
348     return bidirectional_iterator<const char*>(&v.cb);
349   }
endFunctionBeginMemberEnd350   constexpr bidirectional_iterator<const char*> end() const { return bidirectional_iterator<const char*>(&ce); }
351 };
352 static_assert( std::is_invocable_v<RangeRBeginT, FunctionBeginMemberEnd&>);
353 static_assert( std::is_invocable_v<RangeRBeginT, FunctionBeginMemberEnd const&>);
354 static_assert( std::is_invocable_v<RangeCRBeginT, FunctionBeginMemberEnd const&>);
355 
356 struct MemberBeginEndDifferentTypes {
357   bidirectional_iterator<int*> begin();
358   bidirectional_iterator<const int*> end();
359 };
360 static_assert(!std::is_invocable_v<RangeRBeginT, MemberBeginEndDifferentTypes&>);
361 static_assert(!std::is_invocable_v<RangeCRBeginT, MemberBeginEndDifferentTypes&>);
362 
363 struct FunctionBeginEndDifferentTypes {
364   friend bidirectional_iterator<int*> begin(FunctionBeginEndDifferentTypes&);
365   friend bidirectional_iterator<const int*> end(FunctionBeginEndDifferentTypes&);
366 };
367 static_assert(!std::is_invocable_v<RangeRBeginT, FunctionBeginEndDifferentTypes&>);
368 static_assert(!std::is_invocable_v<RangeCRBeginT, FunctionBeginEndDifferentTypes&>);
369 
370 struct MemberBeginEndForwardIterators {
371   forward_iterator<int*> begin();
372   forward_iterator<int*> end();
373 };
374 static_assert(!std::is_invocable_v<RangeRBeginT, MemberBeginEndForwardIterators&>);
375 static_assert(!std::is_invocable_v<RangeCRBeginT, MemberBeginEndForwardIterators&>);
376 
377 struct FunctionBeginEndForwardIterators {
378   friend forward_iterator<int*> begin(FunctionBeginEndForwardIterators&);
379   friend forward_iterator<int*> end(FunctionBeginEndForwardIterators&);
380 };
381 static_assert(!std::is_invocable_v<RangeRBeginT, FunctionBeginEndForwardIterators&>);
382 static_assert(!std::is_invocable_v<RangeCRBeginT, FunctionBeginEndForwardIterators&>);
383 
384 struct MemberBeginOnly {
385   bidirectional_iterator<int*> begin() const;
386 };
387 static_assert(!std::is_invocable_v<RangeRBeginT, MemberBeginOnly&>);
388 static_assert(!std::is_invocable_v<RangeCRBeginT, MemberBeginOnly&>);
389 
390 struct FunctionBeginOnly {
391   friend bidirectional_iterator<int*> begin(FunctionBeginOnly&);
392 };
393 static_assert(!std::is_invocable_v<RangeRBeginT, FunctionBeginOnly&>);
394 static_assert(!std::is_invocable_v<RangeCRBeginT, FunctionBeginOnly&>);
395 
396 struct MemberEndOnly {
397   bidirectional_iterator<int*> end() const;
398 };
399 static_assert(!std::is_invocable_v<RangeRBeginT, MemberEndOnly&>);
400 static_assert(!std::is_invocable_v<RangeCRBeginT, MemberEndOnly&>);
401 
402 struct FunctionEndOnly {
403   friend bidirectional_iterator<int*> end(FunctionEndOnly&);
404 };
405 static_assert(!std::is_invocable_v<RangeRBeginT, FunctionEndOnly&>);
406 static_assert(!std::is_invocable_v<RangeCRBeginT, FunctionEndOnly&>);
407 
408 // Make sure there is no clash between the following cases:
409 // - the case that handles classes defining member `rbegin` and `rend` functions;
410 // - the case that handles classes defining `begin` and `end` functions returning reversible iterators.
411 struct MemberBeginAndRBegin {
412   int* begin() const;
413   int* end() const;
414   int* rbegin() const;
415   int* rend() const;
416 };
417 static_assert( std::is_invocable_v<RangeRBeginT, MemberBeginAndRBegin&>);
418 static_assert( std::is_invocable_v<RangeCRBeginT, MemberBeginAndRBegin&>);
419 static_assert( std::same_as<std::invoke_result_t<RangeRBeginT, MemberBeginAndRBegin&>, int*>);
420 static_assert( std::same_as<std::invoke_result_t<RangeCRBeginT, MemberBeginAndRBegin&>, int*>);
421 
testBeginEnd()422 constexpr bool testBeginEnd() {
423   MemberBeginEnd a{};
424   const MemberBeginEnd aa{};
425   assert(base(std::ranges::rbegin(a).base()) == &a.e);
426   assert(base(std::ranges::crbegin(a).base()) == &a.ce);
427   assert(base(std::ranges::rbegin(aa).base()) == &aa.ce);
428   assert(base(std::ranges::crbegin(aa).base()) == &aa.ce);
429 
430   FunctionBeginEnd b{};
431   const FunctionBeginEnd bb{};
432   assert(base(std::ranges::rbegin(b).base()) == &b.e);
433   assert(base(std::ranges::crbegin(b).base()) == &b.ce);
434   assert(base(std::ranges::rbegin(bb).base()) == &bb.ce);
435   assert(base(std::ranges::crbegin(bb).base()) == &bb.ce);
436 
437   MemberBeginFunctionEnd c{};
438   const MemberBeginFunctionEnd cc{};
439   assert(base(std::ranges::rbegin(c).base()) == &c.e);
440   assert(base(std::ranges::crbegin(c).base()) == &c.ce);
441   assert(base(std::ranges::rbegin(cc).base()) == &cc.ce);
442   assert(base(std::ranges::crbegin(cc).base()) == &cc.ce);
443 
444   FunctionBeginMemberEnd d{};
445   const FunctionBeginMemberEnd dd{};
446   assert(base(std::ranges::rbegin(d).base()) == &d.e);
447   assert(base(std::ranges::crbegin(d).base()) == &d.ce);
448   assert(base(std::ranges::rbegin(dd).base()) == &dd.ce);
449   assert(base(std::ranges::crbegin(dd).base()) == &dd.ce);
450 
451   return true;
452 }
453 
454 
455 ASSERT_NOEXCEPT(std::ranges::rbegin(std::declval<int (&)[10]>()));
456 ASSERT_NOEXCEPT(std::ranges::crbegin(std::declval<int (&)[10]>()));
457 
458 struct NoThrowMemberRBegin {
459   ThrowingIterator<int> rbegin() const noexcept; // auto(t.rbegin()) doesn't throw
460 } ntmb;
461 static_assert(noexcept(std::ranges::rbegin(ntmb)));
462 static_assert(noexcept(std::ranges::crbegin(ntmb)));
463 
464 struct NoThrowADLRBegin {
465   friend ThrowingIterator<int> rbegin(NoThrowADLRBegin&) noexcept;  // auto(rbegin(t)) doesn't throw
466   friend ThrowingIterator<int> rbegin(const NoThrowADLRBegin&) noexcept;
467 } ntab;
468 static_assert(noexcept(std::ranges::rbegin(ntab)));
469 static_assert(noexcept(std::ranges::crbegin(ntab)));
470 
471 struct NoThrowMemberRBeginReturnsRef {
472   ThrowingIterator<int>& rbegin() const noexcept; // auto(t.rbegin()) may throw
473 } ntmbrr;
474 static_assert(!noexcept(std::ranges::rbegin(ntmbrr)));
475 static_assert(!noexcept(std::ranges::crbegin(ntmbrr)));
476 
477 struct RBeginReturnsArrayRef {
478     auto rbegin() const noexcept -> int(&)[10];
479 } brar;
480 static_assert(noexcept(std::ranges::rbegin(brar)));
481 static_assert(noexcept(std::ranges::crbegin(brar)));
482 
483 struct NoThrowBeginThrowingEnd {
484   int* begin() const noexcept;
485   int* end() const;
486 } ntbte;
487 static_assert(!noexcept(std::ranges::rbegin(ntbte)));
488 static_assert(!noexcept(std::ranges::crbegin(ntbte)));
489 
490 struct NoThrowEndThrowingBegin {
491   int* begin() const;
492   int* end() const noexcept;
493 } ntetb;
494 static_assert(noexcept(std::ranges::rbegin(ntetb)));
495 static_assert(noexcept(std::ranges::crbegin(ntetb)));
496 
497 // Test ADL-proofing.
498 struct Incomplete;
499 template<class T> struct Holder { T t; };
500 static_assert(!std::is_invocable_v<RangeRBeginT, Holder<Incomplete>*>);
501 static_assert(!std::is_invocable_v<RangeRBeginT, Holder<Incomplete>*&>);
502 static_assert(!std::is_invocable_v<RangeCRBeginT, Holder<Incomplete>*>);
503 static_assert(!std::is_invocable_v<RangeCRBeginT, Holder<Incomplete>*&>);
504 
main(int,char **)505 int main(int, char**) {
506   static_assert(testReturnTypes());
507 
508   testArray();
509   static_assert(testArray());
510 
511   testRBeginMember();
512   static_assert(testRBeginMember());
513 
514   testRBeginFunction();
515   static_assert(testRBeginFunction());
516 
517   testBeginEnd();
518   static_assert(testBeginEnd());
519 
520   return 0;
521 }
522