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