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