//===----------------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // UNSUPPORTED: c++03, c++11, c++14, c++17 // std::ranges::rend // std::ranges::crend #include #include #include #include "test_macros.h" #include "test_iterators.h" using RangeREndT = decltype(std::ranges::rend); using RangeCREndT = decltype(std::ranges::crend); static int globalBuff[8]; static_assert(!std::is_invocable_v); static_assert(!std::is_invocable_v); static_assert(!std::is_invocable_v); static_assert( std::is_invocable_v); static_assert(!std::is_invocable_v); static_assert(!std::is_invocable_v); static_assert(!std::is_invocable_v); static_assert( std::is_invocable_v); struct Incomplete; static_assert(!std::is_invocable_v); static_assert(!std::is_invocable_v); static_assert(!std::is_invocable_v); static_assert(!std::is_invocable_v); struct REndMember { int x; const int* rbegin() const; constexpr const int* rend() const { return &x; } }; // Ensure that we can't call with rvalues with borrowing disabled. static_assert( std::is_invocable_v); static_assert(!std::is_invocable_v); static_assert( std::is_invocable_v); static_assert(!std::is_invocable_v); static_assert( std::is_invocable_v); static_assert(!std::is_invocable_v); static_assert( std::is_invocable_v); static_assert(!std::is_invocable_v); constexpr bool testReturnTypes() { { int *x[2]; ASSERT_SAME_TYPE(decltype(std::ranges::rend(x)), std::reverse_iterator); ASSERT_SAME_TYPE(decltype(std::ranges::crend(x)), std::reverse_iterator); } { int x[2][2]; ASSERT_SAME_TYPE(decltype(std::ranges::rend(x)), std::reverse_iterator); ASSERT_SAME_TYPE(decltype(std::ranges::crend(x)), std::reverse_iterator); } { struct Different { char* rbegin(); sentinel_wrapper& rend(); short* rbegin() const; sentinel_wrapper& rend() const; } x; ASSERT_SAME_TYPE(decltype(std::ranges::rend(x)), sentinel_wrapper); ASSERT_SAME_TYPE(decltype(std::ranges::crend(x)), sentinel_wrapper); } return true; } constexpr bool testArray() { int a[2]; assert(std::ranges::rend(a).base() == a); assert(std::ranges::crend(a).base() == a); int b[2][2]; assert(std::ranges::rend(b).base() == b); assert(std::ranges::crend(b).base() == b); REndMember c[2]; assert(std::ranges::rend(c).base() == c); assert(std::ranges::crend(c).base() == c); return true; } struct REndMemberReturnsInt { int rbegin() const; int rend() const; }; static_assert(!std::is_invocable_v); struct REndMemberReturnsVoidPtr { const void *rbegin() const; const void *rend() const; }; static_assert(!std::is_invocable_v); struct PtrConvertible { operator int*() const; }; struct PtrConvertibleREndMember { PtrConvertible rbegin() const; PtrConvertible rend() const; }; static_assert(!std::is_invocable_v); struct NoRBeginMember { constexpr const int* rend(); }; static_assert(!std::is_invocable_v); struct NonConstREndMember { int x; constexpr int* rbegin() { return nullptr; } constexpr int* rend() { return &x; } }; static_assert( std::is_invocable_v); static_assert(!std::is_invocable_v); static_assert(!std::is_invocable_v); static_assert(!std::is_invocable_v); struct EnabledBorrowingREndMember { constexpr int* rbegin() const { return nullptr; } constexpr int* rend() const { return &globalBuff[0]; } }; template <> inline constexpr bool std::ranges::enable_borrowed_range = true; struct REndMemberFunction { int x; constexpr const int* rbegin() const { return nullptr; } constexpr const int* rend() const { return &x; } friend constexpr int* rend(REndMemberFunction const&); }; struct Empty { }; struct EmptyEndMember { Empty rbegin() const; Empty rend() const; }; static_assert(!std::is_invocable_v); struct EmptyPtrREndMember { Empty x; constexpr const Empty* rbegin() const { return nullptr; } constexpr const Empty* rend() const { return &x; } }; constexpr bool testREndMember() { REndMember a; assert(std::ranges::rend(a) == &a.x); assert(std::ranges::crend(a) == &a.x); NonConstREndMember b; assert(std::ranges::rend(b) == &b.x); static_assert(!std::is_invocable_v); EnabledBorrowingREndMember c; assert(std::ranges::rend(std::move(c)) == &globalBuff[0]); assert(std::ranges::crend(std::move(c)) == &globalBuff[0]); REndMemberFunction d; assert(std::ranges::rend(d) == &d.x); assert(std::ranges::crend(d) == &d.x); EmptyPtrREndMember e; assert(std::ranges::rend(e) == &e.x); assert(std::ranges::crend(e) == &e.x); return true; } struct REndFunction { int x; friend constexpr const int* rbegin(REndFunction const&) { return nullptr; } friend constexpr const int* rend(REndFunction const& bf) { return &bf.x; } }; static_assert( std::is_invocable_v); static_assert(!std::is_invocable_v); static_assert( std::is_invocable_v); static_assert(!std::is_invocable_v); static_assert(!std::is_invocable_v); static_assert( std::is_invocable_v); static_assert( std::is_invocable_v); struct REndFunctionReturnsInt { friend constexpr int rbegin(REndFunctionReturnsInt const&); friend constexpr int rend(REndFunctionReturnsInt const&); }; static_assert(!std::is_invocable_v); struct REndFunctionReturnsVoidPtr { friend constexpr void* rbegin(REndFunctionReturnsVoidPtr const&); friend constexpr void* rend(REndFunctionReturnsVoidPtr const&); }; static_assert(!std::is_invocable_v); struct REndFunctionReturnsEmpty { friend constexpr Empty rbegin(REndFunctionReturnsEmpty const&); friend constexpr Empty rend(REndFunctionReturnsEmpty const&); }; static_assert(!std::is_invocable_v); struct REndFunctionReturnsPtrConvertible { friend constexpr PtrConvertible rbegin(REndFunctionReturnsPtrConvertible const&); friend constexpr PtrConvertible rend(REndFunctionReturnsPtrConvertible const&); }; static_assert(!std::is_invocable_v); struct NoRBeginFunction { friend constexpr const int* rend(NoRBeginFunction const&); }; static_assert(!std::is_invocable_v); struct REndFunctionByValue { friend constexpr int* rbegin(REndFunctionByValue) { return nullptr; } friend constexpr int* rend(REndFunctionByValue) { return &globalBuff[1]; } }; static_assert(!std::is_invocable_v); struct REndFunctionEnabledBorrowing { friend constexpr int* rbegin(REndFunctionEnabledBorrowing) { return nullptr; } friend constexpr int* rend(REndFunctionEnabledBorrowing) { return &globalBuff[2]; } }; template<> inline constexpr bool std::ranges::enable_borrowed_range = true; struct REndFunctionReturnsEmptyPtr { Empty x; friend constexpr const Empty* rbegin(REndFunctionReturnsEmptyPtr const&) { return nullptr; } friend constexpr const Empty* rend(REndFunctionReturnsEmptyPtr const& bf) { return &bf.x; } }; struct REndFunctionWithDataMember { int x; int rend; friend constexpr const int* rbegin(REndFunctionWithDataMember const&) { return nullptr; } friend constexpr const int* rend(REndFunctionWithDataMember const& bf) { return &bf.x; } }; struct REndFunctionWithPrivateEndMember : private REndMember { int y; friend constexpr const int* rbegin(REndFunctionWithPrivateEndMember const&) { return nullptr; } friend constexpr const int* rend(REndFunctionWithPrivateEndMember const& bf) { return &bf.y; } }; struct RBeginMemberEndFunction { int x; constexpr const int* rbegin() const { return nullptr; } friend constexpr const int* rend(RBeginMemberEndFunction const& bf) { return &bf.x; } }; constexpr bool testREndFunction() { const REndFunction a{}; assert(std::ranges::rend(a) == &a.x); assert(std::ranges::crend(a) == &a.x); REndFunction aa{}; static_assert(!std::is_invocable_v); assert(std::ranges::crend(aa) == &aa.x); REndFunctionByValue b; assert(std::ranges::rend(b) == &globalBuff[1]); assert(std::ranges::crend(b) == &globalBuff[1]); REndFunctionEnabledBorrowing c; assert(std::ranges::rend(std::move(c)) == &globalBuff[2]); assert(std::ranges::crend(std::move(c)) == &globalBuff[2]); const REndFunctionReturnsEmptyPtr d{}; assert(std::ranges::rend(d) == &d.x); assert(std::ranges::crend(d) == &d.x); REndFunctionReturnsEmptyPtr dd{}; static_assert(!std::is_invocable_v); assert(std::ranges::crend(dd) == &dd.x); const REndFunctionWithDataMember e{}; assert(std::ranges::rend(e) == &e.x); assert(std::ranges::crend(e) == &e.x); REndFunctionWithDataMember ee{}; static_assert(!std::is_invocable_v); assert(std::ranges::crend(ee) == &ee.x); const REndFunctionWithPrivateEndMember f{}; assert(std::ranges::rend(f) == &f.y); assert(std::ranges::crend(f) == &f.y); REndFunctionWithPrivateEndMember ff{}; static_assert(!std::is_invocable_v); assert(std::ranges::crend(ff) == &ff.y); const RBeginMemberEndFunction g{}; assert(std::ranges::rend(g) == &g.x); assert(std::ranges::crend(g) == &g.x); RBeginMemberEndFunction gg{}; static_assert(!std::is_invocable_v); assert(std::ranges::crend(gg) == &gg.x); return true; } struct MemberBeginEnd { int b, e; char cb, ce; constexpr bidirectional_iterator begin() { return bidirectional_iterator(&b); } constexpr bidirectional_iterator end() { return bidirectional_iterator(&e); } constexpr bidirectional_iterator begin() const { return bidirectional_iterator(&cb); } constexpr bidirectional_iterator end() const { return bidirectional_iterator(&ce); } }; static_assert( std::is_invocable_v); static_assert( std::is_invocable_v); static_assert( std::is_invocable_v); struct FunctionBeginEnd { int b, e; char cb, ce; friend constexpr bidirectional_iterator begin(FunctionBeginEnd& v) { return bidirectional_iterator(&v.b); } friend constexpr bidirectional_iterator end(FunctionBeginEnd& v) { return bidirectional_iterator(&v.e); } friend constexpr bidirectional_iterator begin(const FunctionBeginEnd& v) { return bidirectional_iterator(&v.cb); } friend constexpr bidirectional_iterator end(const FunctionBeginEnd& v) { return bidirectional_iterator(&v.ce); } }; static_assert( std::is_invocable_v); static_assert( std::is_invocable_v); static_assert( std::is_invocable_v); struct MemberBeginFunctionEnd { int b, e; char cb, ce; constexpr bidirectional_iterator begin() { return bidirectional_iterator(&b); } friend constexpr bidirectional_iterator end(MemberBeginFunctionEnd& v) { return bidirectional_iterator(&v.e); } constexpr bidirectional_iterator begin() const { return bidirectional_iterator(&cb); } friend constexpr bidirectional_iterator end(const MemberBeginFunctionEnd& v) { return bidirectional_iterator(&v.ce); } }; static_assert( std::is_invocable_v); static_assert( std::is_invocable_v); static_assert( std::is_invocable_v); struct FunctionBeginMemberEnd { int b, e; char cb, ce; friend constexpr bidirectional_iterator begin(FunctionBeginMemberEnd& v) { return bidirectional_iterator(&v.b); } constexpr bidirectional_iterator end() { return bidirectional_iterator(&e); } friend constexpr bidirectional_iterator begin(const FunctionBeginMemberEnd& v) { return bidirectional_iterator(&v.cb); } constexpr bidirectional_iterator end() const { return bidirectional_iterator(&ce); } }; static_assert( std::is_invocable_v); static_assert( std::is_invocable_v); static_assert( std::is_invocable_v); struct MemberBeginEndDifferentTypes { bidirectional_iterator begin(); bidirectional_iterator end(); }; static_assert(!std::is_invocable_v); static_assert(!std::is_invocable_v); struct FunctionBeginEndDifferentTypes { friend bidirectional_iterator begin(FunctionBeginEndDifferentTypes&); friend bidirectional_iterator end(FunctionBeginEndDifferentTypes&); }; static_assert(!std::is_invocable_v); static_assert(!std::is_invocable_v); struct MemberBeginEndForwardIterators { forward_iterator begin(); forward_iterator end(); }; static_assert(!std::is_invocable_v); static_assert(!std::is_invocable_v); struct FunctionBeginEndForwardIterators { friend forward_iterator begin(FunctionBeginEndForwardIterators&); friend forward_iterator end(FunctionBeginEndForwardIterators&); }; static_assert(!std::is_invocable_v); static_assert(!std::is_invocable_v); struct MemberBeginOnly { bidirectional_iterator begin() const; }; static_assert(!std::is_invocable_v); static_assert(!std::is_invocable_v); struct FunctionBeginOnly { friend bidirectional_iterator begin(FunctionBeginOnly&); }; static_assert(!std::is_invocable_v); static_assert(!std::is_invocable_v); struct MemberEndOnly { bidirectional_iterator end() const; }; static_assert(!std::is_invocable_v); static_assert(!std::is_invocable_v); struct FunctionEndOnly { friend bidirectional_iterator end(FunctionEndOnly&); }; static_assert(!std::is_invocable_v); static_assert(!std::is_invocable_v); // Make sure there is no clash between the following cases: // - the case that handles classes defining member `rbegin` and `rend` functions; // - the case that handles classes defining `begin` and `end` functions returning reversible iterators. struct MemberBeginAndRBegin { int* begin() const; int* end() const; int* rbegin() const; int* rend() const; }; static_assert( std::is_invocable_v); static_assert( std::is_invocable_v); static_assert( std::same_as, int*>); static_assert( std::same_as, int*>); constexpr bool testBeginEnd() { MemberBeginEnd a{}; const MemberBeginEnd aa{}; assert(base(std::ranges::rend(a).base()) == &a.b); assert(base(std::ranges::crend(a).base()) == &a.cb); assert(base(std::ranges::rend(aa).base()) == &aa.cb); assert(base(std::ranges::crend(aa).base()) == &aa.cb); FunctionBeginEnd b{}; const FunctionBeginEnd bb{}; assert(base(std::ranges::rend(b).base()) == &b.b); assert(base(std::ranges::crend(b).base()) == &b.cb); assert(base(std::ranges::rend(bb).base()) == &bb.cb); assert(base(std::ranges::crend(bb).base()) == &bb.cb); MemberBeginFunctionEnd c{}; const MemberBeginFunctionEnd cc{}; assert(base(std::ranges::rend(c).base()) == &c.b); assert(base(std::ranges::crend(c).base()) == &c.cb); assert(base(std::ranges::rend(cc).base()) == &cc.cb); assert(base(std::ranges::crend(cc).base()) == &cc.cb); FunctionBeginMemberEnd d{}; const FunctionBeginMemberEnd dd{}; assert(base(std::ranges::rend(d).base()) == &d.b); assert(base(std::ranges::crend(d).base()) == &d.cb); assert(base(std::ranges::rend(dd).base()) == &dd.cb); assert(base(std::ranges::crend(dd).base()) == &dd.cb); return true; } ASSERT_NOEXCEPT(std::ranges::rend(std::declval())); ASSERT_NOEXCEPT(std::ranges::crend(std::declval())); struct NoThrowMemberREnd { ThrowingIterator rbegin() const; ThrowingIterator rend() const noexcept; // auto(t.rend()) doesn't throw } ntmre; static_assert(noexcept(std::ranges::rend(ntmre))); static_assert(noexcept(std::ranges::crend(ntmre))); struct NoThrowADLREnd { ThrowingIterator rbegin() const; friend ThrowingIterator rend(NoThrowADLREnd&) noexcept; // auto(rend(t)) doesn't throw friend ThrowingIterator rend(const NoThrowADLREnd&) noexcept; } ntare; static_assert(noexcept(std::ranges::rend(ntare))); static_assert(noexcept(std::ranges::crend(ntare))); struct NoThrowMemberREndReturnsRef { ThrowingIterator rbegin() const; ThrowingIterator& rend() const noexcept; // auto(t.rend()) may throw } ntmrerr; static_assert(!noexcept(std::ranges::rend(ntmrerr))); static_assert(!noexcept(std::ranges::crend(ntmrerr))); struct REndReturnsArrayRef { auto rbegin() const noexcept -> int(&)[10]; auto rend() const noexcept -> int(&)[10]; } rerar; static_assert(noexcept(std::ranges::rend(rerar))); static_assert(noexcept(std::ranges::crend(rerar))); struct NoThrowBeginThrowingEnd { int* begin() const noexcept; int* end() const; } ntbte; static_assert(noexcept(std::ranges::rend(ntbte))); static_assert(noexcept(std::ranges::crend(ntbte))); struct NoThrowEndThrowingBegin { int* begin() const; int* end() const noexcept; } ntetb; static_assert(!noexcept(std::ranges::rend(ntetb))); static_assert(!noexcept(std::ranges::crend(ntetb))); // Test ADL-proofing. struct Incomplete; template struct Holder { T t; }; static_assert(!std::is_invocable_v*>); static_assert(!std::is_invocable_v*&>); static_assert(!std::is_invocable_v*>); static_assert(!std::is_invocable_v*&>); int main(int, char**) { static_assert(testReturnTypes()); testArray(); static_assert(testArray()); testREndMember(); static_assert(testREndMember()); testREndFunction(); static_assert(testREndFunction()); testBeginEnd(); static_assert(testBeginEnd()); return 0; }