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