//===----------------------------------------------------------------------===// // // 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 // SPDX-FileCopyrightText: Copyright (c) 2023 NVIDIA CORPORATION & AFFILIATES. // //===----------------------------------------------------------------------===// // UNSUPPORTED: msvc-19.16 // cuda::std::ranges::rend // cuda::std::ranges::crend #include #include #include #include "test_iterators.h" #include "test_macros.h" using RangeREndT = decltype(cuda::std::ranges::rend); using RangeCREndT = decltype(cuda::std::ranges::crend); TEST_GLOBAL_VARIABLE int globalBuff[8]; static_assert(!cuda::std::is_invocable_v); static_assert(!cuda::std::is_invocable_v); static_assert(!cuda::std::is_invocable_v); static_assert(cuda::std::is_invocable_v); static_assert(!cuda::std::is_invocable_v); static_assert(!cuda::std::is_invocable_v); static_assert(!cuda::std::is_invocable_v); static_assert(cuda::std::is_invocable_v); struct Incomplete; static_assert(!cuda::std::is_invocable_v); static_assert(!cuda::std::is_invocable_v); static_assert(!cuda::std::is_invocable_v); static_assert(!cuda::std::is_invocable_v); struct REndMember { int x; __host__ __device__ const int* rbegin() const; __host__ __device__ constexpr const int* rend() const { return &x; } }; // Ensure that we can't call with rvalues with borrowing disabled. static_assert(cuda::std::is_invocable_v); static_assert(!cuda::std::is_invocable_v); static_assert(cuda::std::is_invocable_v); static_assert(!cuda::std::is_invocable_v); static_assert(cuda::std::is_invocable_v); static_assert(!cuda::std::is_invocable_v); static_assert(cuda::std::is_invocable_v); static_assert(!cuda::std::is_invocable_v); struct Different { __host__ __device__ char* rbegin(); __host__ __device__ sentinel_wrapper& rend(); __host__ __device__ short* rbegin() const; __host__ __device__ sentinel_wrapper& rend() const; }; __host__ __device__ constexpr bool testReturnTypes() { { int* x[2] = {}; unused(x); static_assert(cuda::std::is_same_v>); static_assert( cuda::std::is_same_v>); } { int x[2][2] = {}; unused(x); static_assert(cuda::std::is_same_v>); static_assert( cuda::std::is_same_v>); } { Different x{}; unused(x); static_assert(cuda::std::is_same_v>); static_assert(cuda::std::is_same_v>); } return true; } __host__ __device__ constexpr bool testArray() { int a[2] = {}; assert(cuda::std::ranges::rend(a).base() == a); assert(cuda::std::ranges::crend(a).base() == a); int b[2][2] = {}; assert(cuda::std::ranges::rend(b).base() == b); assert(cuda::std::ranges::crend(b).base() == b); REndMember c[2] = {}; assert(cuda::std::ranges::rend(c).base() == c); assert(cuda::std::ranges::crend(c).base() == c); return true; } struct REndMemberReturnsInt { __host__ __device__ int rbegin() const; __host__ __device__ int rend() const; }; static_assert(!cuda::std::is_invocable_v); struct REndMemberReturnsVoidPtr { __host__ __device__ const void* rbegin() const; __host__ __device__ const void* rend() const; }; static_assert(!cuda::std::is_invocable_v); struct PtrConvertible { __host__ __device__ operator int*() const; }; struct PtrConvertibleREndMember { __host__ __device__ PtrConvertible rbegin() const; __host__ __device__ PtrConvertible rend() const; }; static_assert(!cuda::std::is_invocable_v); struct NoRBeginMember { __host__ __device__ constexpr const int* rend(); }; static_assert(!cuda::std::is_invocable_v); struct NonConstREndMember { int x; __host__ __device__ constexpr int* rbegin() { return nullptr; } __host__ __device__ constexpr int* rend() { return &x; } }; static_assert(cuda::std::is_invocable_v); static_assert(!cuda::std::is_invocable_v); static_assert(!cuda::std::is_invocable_v); static_assert(!cuda::std::is_invocable_v); struct EnabledBorrowingREndMember { __host__ __device__ constexpr int* rbegin() const { return nullptr; } __host__ __device__ constexpr int* rend() const { return &globalBuff[0]; } }; template <> inline constexpr bool cuda::std::ranges::enable_borrowed_range = true; struct REndMemberFunction { int x; __host__ __device__ constexpr const int* rbegin() const { return nullptr; } __host__ __device__ constexpr const int* rend() const { return &x; } __host__ __device__ friend constexpr int* rend(REndMemberFunction const&); }; struct Empty {}; struct EmptyEndMember { __host__ __device__ Empty rbegin() const; __host__ __device__ Empty rend() const; }; static_assert(!cuda::std::is_invocable_v); struct EmptyPtrREndMember { Empty x; __host__ __device__ constexpr const Empty* rbegin() const { return nullptr; } __host__ __device__ constexpr const Empty* rend() const { return &x; } }; __host__ __device__ constexpr bool testREndMember() { REndMember a{}; assert(cuda::std::ranges::rend(a) == &a.x); assert(cuda::std::ranges::crend(a) == &a.x); NonConstREndMember b{}; assert(cuda::std::ranges::rend(b) == &b.x); static_assert(!cuda::std::is_invocable_v); EnabledBorrowingREndMember c{}; assert(cuda::std::ranges::rend(cuda::std::move(c)) == &globalBuff[0]); assert(cuda::std::ranges::crend(cuda::std::move(c)) == &globalBuff[0]); REndMemberFunction d{}; assert(cuda::std::ranges::rend(d) == &d.x); assert(cuda::std::ranges::crend(d) == &d.x); EmptyPtrREndMember e{}; assert(cuda::std::ranges::rend(e) == &e.x); assert(cuda::std::ranges::crend(e) == &e.x); return true; } struct REndFunction { int x; __host__ __device__ friend constexpr const int* rbegin(REndFunction const&) { return nullptr; } __host__ __device__ friend constexpr const int* rend(REndFunction const& bf) { return &bf.x; } }; static_assert(cuda::std::is_invocable_v); static_assert(!cuda::std::is_invocable_v); static_assert(cuda::std::is_invocable_v); static_assert(!cuda::std::is_invocable_v); static_assert(!cuda::std::is_invocable_v); static_assert(cuda::std::is_invocable_v); static_assert(cuda::std::is_invocable_v); struct REndFunctionReturnsInt { __host__ __device__ friend constexpr int rbegin(REndFunctionReturnsInt const&); __host__ __device__ friend constexpr int rend(REndFunctionReturnsInt const&); }; static_assert(!cuda::std::is_invocable_v); struct REndFunctionReturnsVoidPtr { __host__ __device__ friend constexpr void* rbegin(REndFunctionReturnsVoidPtr const&); __host__ __device__ friend constexpr void* rend(REndFunctionReturnsVoidPtr const&); }; static_assert(!cuda::std::is_invocable_v); struct REndFunctionReturnsEmpty { __host__ __device__ friend constexpr Empty rbegin(REndFunctionReturnsEmpty const&); __host__ __device__ friend constexpr Empty rend(REndFunctionReturnsEmpty const&); }; static_assert(!cuda::std::is_invocable_v); struct REndFunctionReturnsPtrConvertible { __host__ __device__ friend constexpr PtrConvertible rbegin(REndFunctionReturnsPtrConvertible const&); __host__ __device__ friend constexpr PtrConvertible rend(REndFunctionReturnsPtrConvertible const&); }; static_assert(!cuda::std::is_invocable_v); struct NoRBeginFunction { __host__ __device__ friend constexpr const int* rend(NoRBeginFunction const&); }; static_assert(!cuda::std::is_invocable_v); struct REndFunctionByValue { __host__ __device__ friend constexpr int* rbegin(REndFunctionByValue) { return nullptr; } __host__ __device__ friend constexpr int* rend(REndFunctionByValue) { return &globalBuff[1]; } }; static_assert(!cuda::std::is_invocable_v); struct REndFunctionEnabledBorrowing { __host__ __device__ friend constexpr int* rbegin(REndFunctionEnabledBorrowing) { return nullptr; } __host__ __device__ friend constexpr int* rend(REndFunctionEnabledBorrowing) { return &globalBuff[2]; } }; template <> inline constexpr bool cuda::std::ranges::enable_borrowed_range = true; struct REndFunctionReturnsEmptyPtr { Empty x; __host__ __device__ friend constexpr const Empty* rbegin(REndFunctionReturnsEmptyPtr const&) { return nullptr; } __host__ __device__ friend constexpr const Empty* rend(REndFunctionReturnsEmptyPtr const& bf) { return &bf.x; } }; struct REndFunctionWithDataMember { int x; int rend; __host__ __device__ friend constexpr const int* rbegin(REndFunctionWithDataMember const&) { return nullptr; } __host__ __device__ friend constexpr const int* rend(REndFunctionWithDataMember const& bf) { return &bf.x; } }; struct REndFunctionWithPrivateEndMember : private REndMember { int y; __host__ __device__ friend constexpr const int* rbegin(REndFunctionWithPrivateEndMember const&) { return nullptr; } __host__ __device__ friend constexpr const int* rend(REndFunctionWithPrivateEndMember const& bf) { return &bf.y; } }; struct RBeginMemberEndFunction { int x; __host__ __device__ constexpr const int* rbegin() const { return nullptr; } __host__ __device__ friend constexpr const int* rend(RBeginMemberEndFunction const& bf) { return &bf.x; } }; __host__ __device__ constexpr bool testREndFunction() { const REndFunction a{}; assert(cuda::std::ranges::rend(a) == &a.x); assert(cuda::std::ranges::crend(a) == &a.x); REndFunction aa{}; static_assert(!cuda::std::is_invocable_v); assert(cuda::std::ranges::crend(aa) == &aa.x); REndFunctionByValue b{}; assert(cuda::std::ranges::rend(b) == &globalBuff[1]); assert(cuda::std::ranges::crend(b) == &globalBuff[1]); REndFunctionEnabledBorrowing c{}; assert(cuda::std::ranges::rend(cuda::std::move(c)) == &globalBuff[2]); assert(cuda::std::ranges::crend(cuda::std::move(c)) == &globalBuff[2]); const REndFunctionReturnsEmptyPtr d{}; assert(cuda::std::ranges::rend(d) == &d.x); assert(cuda::std::ranges::crend(d) == &d.x); REndFunctionReturnsEmptyPtr dd{}; static_assert(!cuda::std::is_invocable_v); assert(cuda::std::ranges::crend(dd) == &dd.x); const REndFunctionWithDataMember e{}; assert(cuda::std::ranges::rend(e) == &e.x); assert(cuda::std::ranges::crend(e) == &e.x); REndFunctionWithDataMember ee{}; static_assert(!cuda::std::is_invocable_v); assert(cuda::std::ranges::crend(ee) == &ee.x); const REndFunctionWithPrivateEndMember f{}; assert(cuda::std::ranges::rend(f) == &f.y); assert(cuda::std::ranges::crend(f) == &f.y); REndFunctionWithPrivateEndMember ff{}; static_assert(!cuda::std::is_invocable_v); assert(cuda::std::ranges::crend(ff) == &ff.y); const RBeginMemberEndFunction g{}; assert(cuda::std::ranges::rend(g) == &g.x); assert(cuda::std::ranges::crend(g) == &g.x); RBeginMemberEndFunction gg{}; static_assert(!cuda::std::is_invocable_v); assert(cuda::std::ranges::crend(gg) == &gg.x); return true; } struct MemberBeginEnd { int b, e; char cb, ce; __host__ __device__ constexpr bidirectional_iterator begin() { return bidirectional_iterator(&b); } __host__ __device__ constexpr bidirectional_iterator end() { return bidirectional_iterator(&e); } __host__ __device__ constexpr bidirectional_iterator begin() const { return bidirectional_iterator(&cb); } __host__ __device__ constexpr bidirectional_iterator end() const { return bidirectional_iterator(&ce); } }; static_assert(cuda::std::is_invocable_v); static_assert(cuda::std::is_invocable_v); static_assert(cuda::std::is_invocable_v); struct FunctionBeginEnd { int b, e; char cb, ce; __host__ __device__ friend constexpr bidirectional_iterator begin(FunctionBeginEnd& v) { return bidirectional_iterator(&v.b); } __host__ __device__ friend constexpr bidirectional_iterator end(FunctionBeginEnd& v) { return bidirectional_iterator(&v.e); } __host__ __device__ friend constexpr bidirectional_iterator begin(const FunctionBeginEnd& v) { return bidirectional_iterator(&v.cb); } __host__ __device__ friend constexpr bidirectional_iterator end(const FunctionBeginEnd& v) { return bidirectional_iterator(&v.ce); } }; static_assert(cuda::std::is_invocable_v); static_assert(cuda::std::is_invocable_v); static_assert(cuda::std::is_invocable_v); struct MemberBeginFunctionEnd { int b, e; char cb, ce; __host__ __device__ constexpr bidirectional_iterator begin() { return bidirectional_iterator(&b); } __host__ __device__ friend constexpr bidirectional_iterator end(MemberBeginFunctionEnd& v) { return bidirectional_iterator(&v.e); } __host__ __device__ constexpr bidirectional_iterator begin() const { return bidirectional_iterator(&cb); } __host__ __device__ friend constexpr bidirectional_iterator end(const MemberBeginFunctionEnd& v) { return bidirectional_iterator(&v.ce); } }; static_assert(cuda::std::is_invocable_v); static_assert(cuda::std::is_invocable_v); static_assert(cuda::std::is_invocable_v); struct FunctionBeginMemberEnd { int b, e; char cb, ce; __host__ __device__ friend constexpr bidirectional_iterator begin(FunctionBeginMemberEnd& v) { return bidirectional_iterator(&v.b); } __host__ __device__ constexpr bidirectional_iterator end() { return bidirectional_iterator(&e); } __host__ __device__ friend constexpr bidirectional_iterator begin(const FunctionBeginMemberEnd& v) { return bidirectional_iterator(&v.cb); } __host__ __device__ constexpr bidirectional_iterator end() const { return bidirectional_iterator(&ce); } }; static_assert(cuda::std::is_invocable_v); static_assert(cuda::std::is_invocable_v); static_assert(cuda::std::is_invocable_v); struct MemberBeginEndDifferentTypes { __host__ __device__ bidirectional_iterator begin(); __host__ __device__ bidirectional_iterator end(); }; static_assert(!cuda::std::is_invocable_v); static_assert(!cuda::std::is_invocable_v); struct FunctionBeginEndDifferentTypes { __host__ __device__ friend bidirectional_iterator begin(FunctionBeginEndDifferentTypes&); __host__ __device__ friend bidirectional_iterator end(FunctionBeginEndDifferentTypes&); }; static_assert(!cuda::std::is_invocable_v); static_assert(!cuda::std::is_invocable_v); struct MemberBeginEndForwardIterators { __host__ __device__ forward_iterator begin(); __host__ __device__ forward_iterator end(); }; static_assert(!cuda::std::is_invocable_v); static_assert(!cuda::std::is_invocable_v); struct FunctionBeginEndForwardIterators { __host__ __device__ friend forward_iterator begin(FunctionBeginEndForwardIterators&); __host__ __device__ friend forward_iterator end(FunctionBeginEndForwardIterators&); }; static_assert(!cuda::std::is_invocable_v); static_assert(!cuda::std::is_invocable_v); struct MemberBeginOnly { __host__ __device__ bidirectional_iterator begin() const; }; static_assert(!cuda::std::is_invocable_v); static_assert(!cuda::std::is_invocable_v); struct FunctionBeginOnly { __host__ __device__ friend bidirectional_iterator begin(FunctionBeginOnly&); }; static_assert(!cuda::std::is_invocable_v); static_assert(!cuda::std::is_invocable_v); struct MemberEndOnly { __host__ __device__ bidirectional_iterator end() const; }; static_assert(!cuda::std::is_invocable_v); static_assert(!cuda::std::is_invocable_v); struct FunctionEndOnly { __host__ __device__ friend bidirectional_iterator end(FunctionEndOnly&); }; static_assert(!cuda::std::is_invocable_v); static_assert(!cuda::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 { __host__ __device__ int* begin() const; __host__ __device__ int* end() const; __host__ __device__ int* rbegin() const; __host__ __device__ int* rend() const; }; static_assert(cuda::std::is_invocable_v); static_assert(cuda::std::is_invocable_v); static_assert(cuda::std::same_as, int*>); static_assert(cuda::std::same_as, int*>); __host__ __device__ constexpr bool testBeginEnd() { MemberBeginEnd a{}; const MemberBeginEnd aa{}; assert(base(cuda::std::ranges::rend(a).base()) == &a.b); assert(base(cuda::std::ranges::crend(a).base()) == &a.cb); assert(base(cuda::std::ranges::rend(aa).base()) == &aa.cb); assert(base(cuda::std::ranges::crend(aa).base()) == &aa.cb); FunctionBeginEnd b{}; const FunctionBeginEnd bb{}; assert(base(cuda::std::ranges::rend(b).base()) == &b.b); assert(base(cuda::std::ranges::crend(b).base()) == &b.cb); assert(base(cuda::std::ranges::rend(bb).base()) == &bb.cb); assert(base(cuda::std::ranges::crend(bb).base()) == &bb.cb); MemberBeginFunctionEnd c{}; const MemberBeginFunctionEnd cc{}; assert(base(cuda::std::ranges::rend(c).base()) == &c.b); assert(base(cuda::std::ranges::crend(c).base()) == &c.cb); assert(base(cuda::std::ranges::rend(cc).base()) == &cc.cb); assert(base(cuda::std::ranges::crend(cc).base()) == &cc.cb); FunctionBeginMemberEnd d{}; const FunctionBeginMemberEnd dd{}; assert(base(cuda::std::ranges::rend(d).base()) == &d.b); assert(base(cuda::std::ranges::crend(d).base()) == &d.cb); assert(base(cuda::std::ranges::rend(dd).base()) == &dd.cb); assert(base(cuda::std::ranges::crend(dd).base()) == &dd.cb); return true; } static_assert(noexcept(cuda::std::ranges::rend(cuda::std::declval()))); static_assert(noexcept(cuda::std::ranges::crend(cuda::std::declval()))); #if !TEST_COMPILER(MSVC2019) _CCCL_GLOBAL_CONSTANT struct NoThrowMemberREnd { __host__ __device__ ThrowingIterator rbegin() const; __host__ __device__ ThrowingIterator rend() const noexcept; // auto(t.rend()) doesn't throw } ntmre; static_assert(noexcept(cuda::std::ranges::rend(ntmre))); static_assert(noexcept(cuda::std::ranges::crend(ntmre))); _CCCL_GLOBAL_CONSTANT struct NoThrowADLREnd { __host__ __device__ ThrowingIterator rbegin() const; __host__ __device__ friend ThrowingIterator rend(NoThrowADLREnd&) noexcept; // auto(rend(t)) doesn't throw __host__ __device__ friend ThrowingIterator rend(const NoThrowADLREnd&) noexcept; } ntare; static_assert(noexcept(cuda::std::ranges::rend(ntare))); static_assert(noexcept(cuda::std::ranges::crend(ntare))); #endif // !TEST_COMPILER(MSVC2019) _CCCL_GLOBAL_CONSTANT struct NoThrowMemberREndReturnsRef { __host__ __device__ ThrowingIterator rbegin() const; __host__ __device__ ThrowingIterator& rend() const noexcept; // auto(t.rend()) may throw } ntmrerr; static_assert(!noexcept(cuda::std::ranges::rend(ntmrerr))); static_assert(!noexcept(cuda::std::ranges::crend(ntmrerr))); _CCCL_GLOBAL_CONSTANT struct REndReturnsArrayRef { __host__ __device__ auto rbegin() const noexcept -> int (&)[10]; __host__ __device__ auto rend() const noexcept -> int (&)[10]; } rerar; static_assert(noexcept(cuda::std::ranges::rend(rerar))); static_assert(noexcept(cuda::std::ranges::crend(rerar))); _CCCL_GLOBAL_CONSTANT struct NoThrowBeginThrowingEnd { __host__ __device__ int* begin() const noexcept; __host__ __device__ int* end() const; } ntbte; static_assert(noexcept(cuda::std::ranges::rend(ntbte))); static_assert(noexcept(cuda::std::ranges::crend(ntbte))); _CCCL_GLOBAL_CONSTANT struct NoThrowEndThrowingBegin { __host__ __device__ int* begin() const; __host__ __device__ int* end() const noexcept; } ntetb; static_assert(!noexcept(cuda::std::ranges::rend(ntetb))); static_assert(!noexcept(cuda::std::ranges::crend(ntetb))); #if TEST_STD_VER > 2017 // Test ADL-proofing. struct Incomplete; template struct Holder { T t; }; static_assert(!cuda::std::is_invocable_v*>); static_assert(!cuda::std::is_invocable_v*&>); static_assert(!cuda::std::is_invocable_v*>); static_assert(!cuda::std::is_invocable_v*&>); #endif // TEST_STD_VER > 2017 int main(int, char**) { static_assert(testReturnTypes()); testArray(); static_assert(testArray()); testREndMember(); static_assert(testREndMember()); testREndFunction(); static_assert(testREndFunction()); testBeginEnd(); static_assert(testBeginEnd()); #if !TEST_COMPILER(MSVC2019) unused(ntmre); unused(ntare); #endif // !TEST_COMPILER(MSVC2019) unused(ntmrerr); unused(rerar); unused(ntbte); unused(ntetb); return 0; }