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
21using RangeRBeginT = decltype(std::ranges::rbegin);
22using RangeCRBeginT = decltype(std::ranges::crbegin);
23
24static int globalBuff[8];
25
26static_assert(!std::is_invocable_v<RangeRBeginT, int (&&)[10]>);
27static_assert( std::is_invocable_v<RangeRBeginT, int (&)[10]>);
28static_assert(!std::is_invocable_v<RangeRBeginT, int (&&)[]>);
29static_assert(!std::is_invocable_v<RangeRBeginT, int (&)[]>);
30static_assert(!std::is_invocable_v<RangeCRBeginT, int (&&)[10]>);
31static_assert( std::is_invocable_v<RangeCRBeginT, int (&)[10]>);
32static_assert(!std::is_invocable_v<RangeCRBeginT, int (&&)[]>);
33static_assert(!std::is_invocable_v<RangeCRBeginT, int (&)[]>);
34
35struct Incomplete;
36
37static_assert(!std::is_invocable_v<RangeRBeginT, Incomplete(&&)[]>);
38static_assert(!std::is_invocable_v<RangeRBeginT, const Incomplete(&&)[]>);
39static_assert(!std::is_invocable_v<RangeCRBeginT, Incomplete(&&)[]>);
40static_assert(!std::is_invocable_v<RangeCRBeginT, const Incomplete(&&)[]>);
41
42static_assert(!std::is_invocable_v<RangeRBeginT, Incomplete(&&)[10]>);
43static_assert(!std::is_invocable_v<RangeRBeginT, const Incomplete(&&)[10]>);
44static_assert(!std::is_invocable_v<RangeCRBeginT, Incomplete(&&)[10]>);
45static_assert(!std::is_invocable_v<RangeCRBeginT, const Incomplete(&&)[10]>);
46
47// This case is IFNDR; we handle it SFINAE-friendly.
48LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeRBeginT, Incomplete(&)[]>);
49LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeRBeginT, const Incomplete(&)[]>);
50LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeCRBeginT, Incomplete(&)[]>);
51LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeCRBeginT, const Incomplete(&)[]>);
52
53// This case is IFNDR; we handle it SFINAE-friendly.
54LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeRBeginT, Incomplete(&)[10]>);
55LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeRBeginT, const Incomplete(&)[10]>);
56LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeCRBeginT, Incomplete(&)[10]>);
57LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeCRBeginT, const Incomplete(&)[10]>);
58
59struct RBeginMember {
60 int x;
61 constexpr const int *rbegin() const { return &x; }
62};
63
64// Ensure that we can't call with rvalues with borrowing disabled.
65static_assert( std::is_invocable_v<RangeRBeginT, RBeginMember &>);
66static_assert(!std::is_invocable_v<RangeRBeginT, RBeginMember &&>);
67static_assert( std::is_invocable_v<RangeRBeginT, RBeginMember const&>);
68static_assert(!std::is_invocable_v<RangeRBeginT, RBeginMember const&&>);
69static_assert( std::is_invocable_v<RangeCRBeginT, RBeginMember &>);
70static_assert(!std::is_invocable_v<RangeCRBeginT, RBeginMember &&>);
71static_assert( std::is_invocable_v<RangeCRBeginT, RBeginMember const&>);
72static_assert(!std::is_invocable_v<RangeCRBeginT, RBeginMember const&&>);
73
74constexpr 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
96constexpr 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
112struct RBeginMemberReturnsInt {
113 int rbegin() const;
114};
115static_assert(!std::is_invocable_v<RangeRBeginT, RBeginMemberReturnsInt const&>);
116
117struct RBeginMemberReturnsVoidPtr {
118 const void *rbegin() const;
119};
120static_assert(!std::is_invocable_v<RangeRBeginT, RBeginMemberReturnsVoidPtr const&>);
121
122struct PtrConvertibleRBeginMember {
123 struct iterator { operator int*() const; };
124 iterator rbegin() const;
125};
126static_assert(!std::is_invocable_v<RangeRBeginT, PtrConvertibleRBeginMember const&>);
127
128struct NonConstRBeginMember {
129 int x;
130 constexpr int* rbegin() { return &x; }
131};
132static_assert( std::is_invocable_v<RangeRBeginT, NonConstRBeginMember &>);
133static_assert(!std::is_invocable_v<RangeRBeginT, NonConstRBeginMember const&>);
134static_assert(!std::is_invocable_v<RangeCRBeginT, NonConstRBeginMember &>);
135static_assert(!std::is_invocable_v<RangeCRBeginT, NonConstRBeginMember const&>);
136
137struct EnabledBorrowingRBeginMember {
138 constexpr int *rbegin() const { return globalBuff; }
139};
140template<>
141inline constexpr bool std::ranges::enable_borrowed_range<EnabledBorrowingRBeginMember> = true;
142
143struct RBeginMemberFunction {
144 int x;
145 constexpr const int *rbegin() const { return &x; }
146 friend int* rbegin(RBeginMemberFunction const&);
147};
148
149struct EmptyPtrRBeginMember {
150 struct Empty {};
151 Empty x;
152 constexpr const Empty* rbegin() const { return &x; }
153};
154
155constexpr 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
184struct RBeginFunction {
185 int x;
186 friend constexpr const int* rbegin(RBeginFunction const& bf) { return &bf.x; }
187};
188static_assert( std::is_invocable_v<RangeRBeginT, RBeginFunction const&>);
189static_assert(!std::is_invocable_v<RangeRBeginT, RBeginFunction &&>);
190static_assert(
191 std::is_invocable_v<RangeRBeginT, RBeginFunction&>); // Ill-formed before P2602R2 Poison Pills are Too Toxic
192static_assert( std::is_invocable_v<RangeCRBeginT, RBeginFunction const&>);
193static_assert( std::is_invocable_v<RangeCRBeginT, RBeginFunction &>);
194
195struct RBeginFunctionReturnsInt {
196 friend int rbegin(RBeginFunctionReturnsInt const&);
197};
198static_assert(!std::is_invocable_v<RangeRBeginT, RBeginFunctionReturnsInt const&>);
199
200struct RBeginFunctionReturnsVoidPtr {
201 friend void *rbegin(RBeginFunctionReturnsVoidPtr const&);
202};
203static_assert(!std::is_invocable_v<RangeRBeginT, RBeginFunctionReturnsVoidPtr const&>);
204
205struct RBeginFunctionReturnsEmpty {
206 struct Empty {};
207 friend Empty rbegin(RBeginFunctionReturnsEmpty const&);
208};
209static_assert(!std::is_invocable_v<RangeRBeginT, RBeginFunctionReturnsEmpty const&>);
210
211struct RBeginFunctionReturnsPtrConvertible {
212 struct iterator { operator int*() const; };
213 friend iterator rbegin(RBeginFunctionReturnsPtrConvertible const&);
214};
215static_assert(!std::is_invocable_v<RangeRBeginT, RBeginFunctionReturnsPtrConvertible const&>);
216
217struct RBeginFunctionByValue {
218 friend constexpr int *rbegin(RBeginFunctionByValue) { return globalBuff + 1; }
219};
220static_assert(!std::is_invocable_v<RangeCRBeginT, RBeginFunctionByValue>);
221
222struct RBeginFunctionEnabledBorrowing {
223 friend constexpr int *rbegin(RBeginFunctionEnabledBorrowing) { return globalBuff + 2; }
224};
225template<>
226inline constexpr bool std::ranges::enable_borrowed_range<RBeginFunctionEnabledBorrowing> = true;
227
228struct RBeginFunctionReturnsEmptyPtr {
229 struct Empty {};
230 Empty x;
231 friend constexpr const Empty *rbegin(RBeginFunctionReturnsEmptyPtr const& bf) { return &bf.x; }
232};
233
234struct RBeginFunctionWithDataMember {
235 int x;
236 int rbegin;
237 friend constexpr const int *rbegin(RBeginFunctionWithDataMember const& bf) { return &bf.x; }
238};
239
240struct RBeginFunctionWithPrivateBeginMember {
241 int y;
242 friend constexpr const int *rbegin(RBeginFunctionWithPrivateBeginMember const& bf) { return &bf.y; }
243private:
244 const int *rbegin() const;
245};
246
247constexpr 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
294struct MemberBeginEnd {
295 int b, e;
296 char cb, ce;
297 constexpr bidirectional_iterator<int*> begin() { return bidirectional_iterator<int*>(&b); }
298 constexpr bidirectional_iterator<int*> end() { return bidirectional_iterator<int*>(&e); }
299 constexpr bidirectional_iterator<const char*> begin() const { return bidirectional_iterator<const char*>(&cb); }
300 constexpr bidirectional_iterator<const char*> end() const { return bidirectional_iterator<const char*>(&ce); }
301};
302static_assert( std::is_invocable_v<RangeRBeginT, MemberBeginEnd&>);
303static_assert( std::is_invocable_v<RangeRBeginT, MemberBeginEnd const&>);
304static_assert( std::is_invocable_v<RangeCRBeginT, MemberBeginEnd const&>);
305
306struct FunctionBeginEnd {
307 int b, e;
308 char cb, ce;
309 friend constexpr bidirectional_iterator<int*> begin(FunctionBeginEnd& v) {
310 return bidirectional_iterator<int*>(&v.b);
311 }
312 friend constexpr bidirectional_iterator<int*> end(FunctionBeginEnd& v) { return bidirectional_iterator<int*>(&v.e); }
313 friend constexpr bidirectional_iterator<const char*> begin(const FunctionBeginEnd& v) {
314 return bidirectional_iterator<const char*>(&v.cb);
315 }
316 friend constexpr bidirectional_iterator<const char*> end(const FunctionBeginEnd& v) {
317 return bidirectional_iterator<const char*>(&v.ce);
318 }
319};
320static_assert( std::is_invocable_v<RangeRBeginT, FunctionBeginEnd&>);
321static_assert( std::is_invocable_v<RangeRBeginT, FunctionBeginEnd const&>);
322static_assert( std::is_invocable_v<RangeCRBeginT, FunctionBeginEnd const&>);
323
324struct MemberBeginFunctionEnd {
325 int b, e;
326 char cb, ce;
327 constexpr bidirectional_iterator<int*> begin() { return bidirectional_iterator<int*>(&b); }
328 friend constexpr bidirectional_iterator<int*> end(MemberBeginFunctionEnd& v) {
329 return bidirectional_iterator<int*>(&v.e);
330 }
331 constexpr bidirectional_iterator<const char*> begin() const { return bidirectional_iterator<const char*>(&cb); }
332 friend constexpr bidirectional_iterator<const char*> end(const MemberBeginFunctionEnd& v) {
333 return bidirectional_iterator<const char*>(&v.ce);
334 }
335};
336static_assert( std::is_invocable_v<RangeRBeginT, MemberBeginFunctionEnd&>);
337static_assert( std::is_invocable_v<RangeRBeginT, MemberBeginFunctionEnd const&>);
338static_assert( std::is_invocable_v<RangeCRBeginT, MemberBeginFunctionEnd const&>);
339
340struct FunctionBeginMemberEnd {
341 int b, e;
342 char cb, ce;
343 friend constexpr bidirectional_iterator<int*> begin(FunctionBeginMemberEnd& v) {
344 return bidirectional_iterator<int*>(&v.b);
345 }
346 constexpr bidirectional_iterator<int*> end() { return bidirectional_iterator<int*>(&e); }
347 friend constexpr bidirectional_iterator<const char*> begin(const FunctionBeginMemberEnd& v) {
348 return bidirectional_iterator<const char*>(&v.cb);
349 }
350 constexpr bidirectional_iterator<const char*> end() const { return bidirectional_iterator<const char*>(&ce); }
351};
352static_assert( std::is_invocable_v<RangeRBeginT, FunctionBeginMemberEnd&>);
353static_assert( std::is_invocable_v<RangeRBeginT, FunctionBeginMemberEnd const&>);
354static_assert( std::is_invocable_v<RangeCRBeginT, FunctionBeginMemberEnd const&>);
355
356struct MemberBeginEndDifferentTypes {
357 bidirectional_iterator<int*> begin();
358 bidirectional_iterator<const int*> end();
359};
360static_assert(!std::is_invocable_v<RangeRBeginT, MemberBeginEndDifferentTypes&>);
361static_assert(!std::is_invocable_v<RangeCRBeginT, MemberBeginEndDifferentTypes&>);
362
363struct FunctionBeginEndDifferentTypes {
364 friend bidirectional_iterator<int*> begin(FunctionBeginEndDifferentTypes&);
365 friend bidirectional_iterator<const int*> end(FunctionBeginEndDifferentTypes&);
366};
367static_assert(!std::is_invocable_v<RangeRBeginT, FunctionBeginEndDifferentTypes&>);
368static_assert(!std::is_invocable_v<RangeCRBeginT, FunctionBeginEndDifferentTypes&>);
369
370struct MemberBeginEndForwardIterators {
371 forward_iterator<int*> begin();
372 forward_iterator<int*> end();
373};
374static_assert(!std::is_invocable_v<RangeRBeginT, MemberBeginEndForwardIterators&>);
375static_assert(!std::is_invocable_v<RangeCRBeginT, MemberBeginEndForwardIterators&>);
376
377struct FunctionBeginEndForwardIterators {
378 friend forward_iterator<int*> begin(FunctionBeginEndForwardIterators&);
379 friend forward_iterator<int*> end(FunctionBeginEndForwardIterators&);
380};
381static_assert(!std::is_invocable_v<RangeRBeginT, FunctionBeginEndForwardIterators&>);
382static_assert(!std::is_invocable_v<RangeCRBeginT, FunctionBeginEndForwardIterators&>);
383
384struct MemberBeginOnly {
385 bidirectional_iterator<int*> begin() const;
386};
387static_assert(!std::is_invocable_v<RangeRBeginT, MemberBeginOnly&>);
388static_assert(!std::is_invocable_v<RangeCRBeginT, MemberBeginOnly&>);
389
390struct FunctionBeginOnly {
391 friend bidirectional_iterator<int*> begin(FunctionBeginOnly&);
392};
393static_assert(!std::is_invocable_v<RangeRBeginT, FunctionBeginOnly&>);
394static_assert(!std::is_invocable_v<RangeCRBeginT, FunctionBeginOnly&>);
395
396struct MemberEndOnly {
397 bidirectional_iterator<int*> end() const;
398};
399static_assert(!std::is_invocable_v<RangeRBeginT, MemberEndOnly&>);
400static_assert(!std::is_invocable_v<RangeCRBeginT, MemberEndOnly&>);
401
402struct FunctionEndOnly {
403 friend bidirectional_iterator<int*> end(FunctionEndOnly&);
404};
405static_assert(!std::is_invocable_v<RangeRBeginT, FunctionEndOnly&>);
406static_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.
411struct MemberBeginAndRBegin {
412 int* begin() const;
413 int* end() const;
414 int* rbegin() const;
415 int* rend() const;
416};
417static_assert( std::is_invocable_v<RangeRBeginT, MemberBeginAndRBegin&>);
418static_assert( std::is_invocable_v<RangeCRBeginT, MemberBeginAndRBegin&>);
419static_assert( std::same_as<std::invoke_result_t<RangeRBeginT, MemberBeginAndRBegin&>, int*>);
420static_assert( std::same_as<std::invoke_result_t<RangeCRBeginT, MemberBeginAndRBegin&>, int*>);
421
422constexpr 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
455ASSERT_NOEXCEPT(std::ranges::rbegin(std::declval<int (&)[10]>()));
456ASSERT_NOEXCEPT(std::ranges::crbegin(std::declval<int (&)[10]>()));
457
458struct NoThrowMemberRBegin {
459 ThrowingIterator<int> rbegin() const noexcept; // auto(t.rbegin()) doesn't throw
460} ntmb;
461static_assert(noexcept(std::ranges::rbegin(ntmb)));
462static_assert(noexcept(std::ranges::crbegin(ntmb)));
463
464struct NoThrowADLRBegin {
465 friend ThrowingIterator<int> rbegin(NoThrowADLRBegin&) noexcept; // auto(rbegin(t)) doesn't throw
466 friend ThrowingIterator<int> rbegin(const NoThrowADLRBegin&) noexcept;
467} ntab;
468static_assert(noexcept(std::ranges::rbegin(ntab)));
469static_assert(noexcept(std::ranges::crbegin(ntab)));
470
471struct NoThrowMemberRBeginReturnsRef {
472 ThrowingIterator<int>& rbegin() const noexcept; // auto(t.rbegin()) may throw
473} ntmbrr;
474static_assert(!noexcept(std::ranges::rbegin(ntmbrr)));
475static_assert(!noexcept(std::ranges::crbegin(ntmbrr)));
476
477struct RBeginReturnsArrayRef {
478 auto rbegin() const noexcept -> int(&)[10];
479} brar;
480static_assert(noexcept(std::ranges::rbegin(brar)));
481static_assert(noexcept(std::ranges::crbegin(brar)));
482
483struct NoThrowBeginThrowingEnd {
484 int* begin() const noexcept;
485 int* end() const;
486} ntbte;
487static_assert(!noexcept(std::ranges::rbegin(ntbte)));
488static_assert(!noexcept(std::ranges::crbegin(ntbte)));
489
490struct NoThrowEndThrowingBegin {
491 int* begin() const;
492 int* end() const noexcept;
493} ntetb;
494static_assert(noexcept(std::ranges::rbegin(ntetb)));
495static_assert(noexcept(std::ranges::crbegin(ntetb)));
496
497// Test ADL-proofing.
498struct Incomplete;
499template<class T> struct Holder { T t; };
500static_assert(!std::is_invocable_v<RangeRBeginT, Holder<Incomplete>*>);
501static_assert(!std::is_invocable_v<RangeRBeginT, Holder<Incomplete>*&>);
502static_assert(!std::is_invocable_v<RangeCRBeginT, Holder<Incomplete>*>);
503static_assert(!std::is_invocable_v<RangeCRBeginT, Holder<Incomplete>*&>);
504
505int 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

source code of libcxx/test/std/ranges/range.access/rbegin.pass.cpp