//===----------------------------------------------------------------------===// // // 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::rbegin // std::ranges::crbegin #include #include #include #include "test_macros.h" #include "test_iterators.h" using RangeRBeginT = decltype(std::ranges::rbegin); using RangeCRBeginT = decltype(std::ranges::crbegin); 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); static_assert(!std::is_invocable_v); static_assert(!std::is_invocable_v); static_assert(!std::is_invocable_v); static_assert(!std::is_invocable_v); // This case is IFNDR; we handle it SFINAE-friendly. LIBCPP_STATIC_ASSERT(!std::is_invocable_v); LIBCPP_STATIC_ASSERT(!std::is_invocable_v); LIBCPP_STATIC_ASSERT(!std::is_invocable_v); LIBCPP_STATIC_ASSERT(!std::is_invocable_v); // This case is IFNDR; we handle it SFINAE-friendly. LIBCPP_STATIC_ASSERT(!std::is_invocable_v); LIBCPP_STATIC_ASSERT(!std::is_invocable_v); LIBCPP_STATIC_ASSERT(!std::is_invocable_v); LIBCPP_STATIC_ASSERT(!std::is_invocable_v); struct RBeginMember { int x; constexpr const int *rbegin() 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::rbegin(x)), std::reverse_iterator); ASSERT_SAME_TYPE(decltype(std::ranges::crbegin(x)), std::reverse_iterator); } { int x[2][2]; ASSERT_SAME_TYPE(decltype(std::ranges::rbegin(x)), std::reverse_iterator); ASSERT_SAME_TYPE(decltype(std::ranges::crbegin(x)), std::reverse_iterator); } { struct Different { char*& rbegin(); short*& rbegin() const; } x; ASSERT_SAME_TYPE(decltype(std::ranges::rbegin(x)), char*); ASSERT_SAME_TYPE(decltype(std::ranges::crbegin(x)), short*); } return true; } constexpr bool testArray() { int a[2]; assert(std::ranges::rbegin(a).base() == a + 2); assert(std::ranges::crbegin(a).base() == a + 2); int b[2][2]; assert(std::ranges::rbegin(b).base() == b + 2); assert(std::ranges::crbegin(b).base() == b + 2); RBeginMember c[2]; assert(std::ranges::rbegin(c).base() == c + 2); assert(std::ranges::crbegin(c).base() == c + 2); return true; } struct RBeginMemberReturnsInt { int rbegin() const; }; static_assert(!std::is_invocable_v); struct RBeginMemberReturnsVoidPtr { const void *rbegin() const; }; static_assert(!std::is_invocable_v); struct PtrConvertibleRBeginMember { struct iterator { operator int*() const; }; iterator rbegin() const; }; static_assert(!std::is_invocable_v); struct NonConstRBeginMember { int x; constexpr int* rbegin() { 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 EnabledBorrowingRBeginMember { constexpr int *rbegin() const { return globalBuff; } }; template<> inline constexpr bool std::ranges::enable_borrowed_range = true; struct RBeginMemberFunction { int x; constexpr const int *rbegin() const { return &x; } friend int* rbegin(RBeginMemberFunction const&); }; struct EmptyPtrRBeginMember { struct Empty {}; Empty x; constexpr const Empty* rbegin() const { return &x; } }; constexpr bool testRBeginMember() { RBeginMember a; assert(std::ranges::rbegin(a) == &a.x); assert(std::ranges::crbegin(a) == &a.x); static_assert(!std::is_invocable_v); static_assert(!std::is_invocable_v); NonConstRBeginMember b; assert(std::ranges::rbegin(b) == &b.x); static_assert(!std::is_invocable_v); EnabledBorrowingRBeginMember c; assert(std::ranges::rbegin(c) == globalBuff); assert(std::ranges::crbegin(c) == globalBuff); assert(std::ranges::rbegin(std::move(c)) == globalBuff); assert(std::ranges::crbegin(std::move(c)) == globalBuff); RBeginMemberFunction d; assert(std::ranges::rbegin(d) == &d.x); assert(std::ranges::crbegin(d) == &d.x); EmptyPtrRBeginMember e; assert(std::ranges::rbegin(e) == &e.x); assert(std::ranges::crbegin(e) == &e.x); return true; } struct RBeginFunction { int x; friend constexpr const int* rbegin(RBeginFunction const& bf) { return &bf.x; } }; static_assert( std::is_invocable_v); static_assert(!std::is_invocable_v); static_assert( std::is_invocable_v); // Ill-formed before P2602R2 Poison Pills are Too Toxic static_assert( std::is_invocable_v); static_assert( std::is_invocable_v); struct RBeginFunctionReturnsInt { friend int rbegin(RBeginFunctionReturnsInt const&); }; static_assert(!std::is_invocable_v); struct RBeginFunctionReturnsVoidPtr { friend void *rbegin(RBeginFunctionReturnsVoidPtr const&); }; static_assert(!std::is_invocable_v); struct RBeginFunctionReturnsEmpty { struct Empty {}; friend Empty rbegin(RBeginFunctionReturnsEmpty const&); }; static_assert(!std::is_invocable_v); struct RBeginFunctionReturnsPtrConvertible { struct iterator { operator int*() const; }; friend iterator rbegin(RBeginFunctionReturnsPtrConvertible const&); }; static_assert(!std::is_invocable_v); struct RBeginFunctionByValue { friend constexpr int *rbegin(RBeginFunctionByValue) { return globalBuff + 1; } }; static_assert(!std::is_invocable_v); struct RBeginFunctionEnabledBorrowing { friend constexpr int *rbegin(RBeginFunctionEnabledBorrowing) { return globalBuff + 2; } }; template<> inline constexpr bool std::ranges::enable_borrowed_range = true; struct RBeginFunctionReturnsEmptyPtr { struct Empty {}; Empty x; friend constexpr const Empty *rbegin(RBeginFunctionReturnsEmptyPtr const& bf) { return &bf.x; } }; struct RBeginFunctionWithDataMember { int x; int rbegin; friend constexpr const int *rbegin(RBeginFunctionWithDataMember const& bf) { return &bf.x; } }; struct RBeginFunctionWithPrivateBeginMember { int y; friend constexpr const int *rbegin(RBeginFunctionWithPrivateBeginMember const& bf) { return &bf.y; } private: const int *rbegin() const; }; constexpr bool testRBeginFunction() { RBeginFunction a{}; const RBeginFunction aa{}; assert(std::ranges::rbegin(a) == &a.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic assert(std::ranges::crbegin(a) == &a.x); assert(std::ranges::rbegin(aa) == &aa.x); assert(std::ranges::crbegin(aa) == &aa.x); RBeginFunctionByValue b{}; const RBeginFunctionByValue bb{}; assert(std::ranges::rbegin(b) == globalBuff + 1); assert(std::ranges::crbegin(b) == globalBuff + 1); assert(std::ranges::rbegin(bb) == globalBuff + 1); assert(std::ranges::crbegin(bb) == globalBuff + 1); RBeginFunctionEnabledBorrowing c{}; const RBeginFunctionEnabledBorrowing cc{}; assert(std::ranges::rbegin(std::move(c)) == globalBuff + 2); assert(std::ranges::crbegin(std::move(c)) == globalBuff + 2); assert(std::ranges::rbegin(std::move(cc)) == globalBuff + 2); assert(std::ranges::crbegin(std::move(cc)) == globalBuff + 2); RBeginFunctionReturnsEmptyPtr d{}; const RBeginFunctionReturnsEmptyPtr dd{}; assert(std::ranges::rbegin(d) == &d.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic assert(std::ranges::crbegin(d) == &d.x); assert(std::ranges::rbegin(dd) == &dd.x); assert(std::ranges::crbegin(dd) == &dd.x); RBeginFunctionWithDataMember e{}; const RBeginFunctionWithDataMember ee{}; assert(std::ranges::rbegin(e) == &e.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic assert(std::ranges::rbegin(ee) == &ee.x); assert(std::ranges::crbegin(e) == &e.x); assert(std::ranges::crbegin(ee) == &ee.x); RBeginFunctionWithPrivateBeginMember f{}; const RBeginFunctionWithPrivateBeginMember ff{}; assert(std::ranges::rbegin(f) == &f.y); // Ill-formed before P2602R2 Poison Pills are Too Toxic assert(std::ranges::crbegin(f) == &f.y); assert(std::ranges::rbegin(ff) == &ff.y); assert(std::ranges::crbegin(ff) == &ff.y); 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::rbegin(a).base()) == &a.e); assert(base(std::ranges::crbegin(a).base()) == &a.ce); assert(base(std::ranges::rbegin(aa).base()) == &aa.ce); assert(base(std::ranges::crbegin(aa).base()) == &aa.ce); FunctionBeginEnd b{}; const FunctionBeginEnd bb{}; assert(base(std::ranges::rbegin(b).base()) == &b.e); assert(base(std::ranges::crbegin(b).base()) == &b.ce); assert(base(std::ranges::rbegin(bb).base()) == &bb.ce); assert(base(std::ranges::crbegin(bb).base()) == &bb.ce); MemberBeginFunctionEnd c{}; const MemberBeginFunctionEnd cc{}; assert(base(std::ranges::rbegin(c).base()) == &c.e); assert(base(std::ranges::crbegin(c).base()) == &c.ce); assert(base(std::ranges::rbegin(cc).base()) == &cc.ce); assert(base(std::ranges::crbegin(cc).base()) == &cc.ce); FunctionBeginMemberEnd d{}; const FunctionBeginMemberEnd dd{}; assert(base(std::ranges::rbegin(d).base()) == &d.e); assert(base(std::ranges::crbegin(d).base()) == &d.ce); assert(base(std::ranges::rbegin(dd).base()) == &dd.ce); assert(base(std::ranges::crbegin(dd).base()) == &dd.ce); return true; } ASSERT_NOEXCEPT(std::ranges::rbegin(std::declval())); ASSERT_NOEXCEPT(std::ranges::crbegin(std::declval())); struct NoThrowMemberRBegin { ThrowingIterator rbegin() const noexcept; // auto(t.rbegin()) doesn't throw } ntmb; static_assert(noexcept(std::ranges::rbegin(ntmb))); static_assert(noexcept(std::ranges::crbegin(ntmb))); struct NoThrowADLRBegin { friend ThrowingIterator rbegin(NoThrowADLRBegin&) noexcept; // auto(rbegin(t)) doesn't throw friend ThrowingIterator rbegin(const NoThrowADLRBegin&) noexcept; } ntab; static_assert(noexcept(std::ranges::rbegin(ntab))); static_assert(noexcept(std::ranges::crbegin(ntab))); struct NoThrowMemberRBeginReturnsRef { ThrowingIterator& rbegin() const noexcept; // auto(t.rbegin()) may throw } ntmbrr; static_assert(!noexcept(std::ranges::rbegin(ntmbrr))); static_assert(!noexcept(std::ranges::crbegin(ntmbrr))); struct RBeginReturnsArrayRef { auto rbegin() const noexcept -> int(&)[10]; } brar; static_assert(noexcept(std::ranges::rbegin(brar))); static_assert(noexcept(std::ranges::crbegin(brar))); struct NoThrowBeginThrowingEnd { int* begin() const noexcept; int* end() const; } ntbte; static_assert(!noexcept(std::ranges::rbegin(ntbte))); static_assert(!noexcept(std::ranges::crbegin(ntbte))); struct NoThrowEndThrowingBegin { int* begin() const; int* end() const noexcept; } ntetb; static_assert(noexcept(std::ranges::rbegin(ntetb))); static_assert(noexcept(std::ranges::crbegin(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()); testRBeginMember(); static_assert(testRBeginMember()); testRBeginFunction(); static_assert(testRBeginFunction()); testBeginEnd(); static_assert(testBeginEnd()); return 0; }