//===----------------------------------------------------------------------===// // // 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. // //===----------------------------------------------------------------------===// // cuda::std::ranges::end // cuda::std::ranges::cend #include #include #include #include "test_iterators.h" #include "test_macros.h" using RangeEndT = decltype(cuda::std::ranges::end); using RangeCEndT = decltype(cuda::std::ranges::cend); TEST_GLOBAL_VARIABLE int globalBuff[8] = {}; #if !TEST_COMPILER(MSVC, <, 19, 23) // old MSVC has a bug where it doesn't properly handle rvalue arrays 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_COMPILER(MSVC, <, 19, 23) 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 EndMember { int x; __host__ __device__ const int* begin() const; __host__ __device__ constexpr const int* end() 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* begin(); __host__ __device__ sentinel_wrapper& end(); __host__ __device__ short* begin() const; __host__ __device__ sentinel_wrapper& end() const; }; __host__ __device__ constexpr bool testReturnTypes() { { int* x[2] = {}; unused(x); static_assert(cuda::std::same_as, ""); static_assert(cuda::std::same_as, ""); } { int x[2][2] = {}; unused(x); static_assert(cuda::std::same_as, ""); static_assert(cuda::std::same_as, ""); } { Different x{}; unused(x); static_assert(cuda::std::same_as>, ""); static_assert(cuda::std::same_as>, ""); } return true; } __host__ __device__ constexpr bool testArray() { int a[2] = {}; assert(cuda::std::ranges::end(a) == a + 2); assert(cuda::std::ranges::cend(a) == a + 2); int b[2][2] = {}; assert(cuda::std::ranges::end(b) == b + 2); assert(cuda::std::ranges::cend(b) == b + 2); EndMember c[2] = {}; assert(cuda::std::ranges::end(c) == c + 2); assert(cuda::std::ranges::cend(c) == c + 2); return true; } struct EndMemberReturnsInt { __host__ __device__ int begin() const; __host__ __device__ int end() const; }; static_assert(!cuda::std::is_invocable_v, ""); struct EndMemberReturnsVoidPtr { __host__ __device__ const void* begin() const; __host__ __device__ const void* end() const; }; static_assert(!cuda::std::is_invocable_v, ""); struct PtrConvertible { __host__ __device__ operator int*() const; }; struct PtrConvertibleEndMember { __host__ __device__ PtrConvertible begin() const; __host__ __device__ PtrConvertible end() const; }; static_assert(!cuda::std::is_invocable_v, ""); struct NoBeginMember { __host__ __device__ constexpr const int* end(); }; static_assert(!cuda::std::is_invocable_v, ""); struct NonConstEndMember { int x; __host__ __device__ constexpr int* begin() { return nullptr; } __host__ __device__ constexpr int* end() { 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 EnabledBorrowingEndMember { __host__ __device__ constexpr int* begin() const { return nullptr; } __host__ __device__ constexpr int* end() const { return &globalBuff[0]; } }; namespace cuda { namespace std { namespace ranges { template <> _CCCL_INLINE_VAR constexpr bool enable_borrowed_range = true; } } // namespace std } // namespace cuda struct EndMemberFunction { int x; __host__ __device__ constexpr const int* begin() const { return nullptr; } __host__ __device__ constexpr const int* end() const { return &x; } __host__ __device__ friend constexpr int* end(EndMemberFunction const&); }; struct Empty {}; struct EmptyEndMember { __host__ __device__ Empty begin() const; __host__ __device__ Empty end() const; }; static_assert(!cuda::std::is_invocable_v, ""); struct EmptyPtrEndMember { Empty x; __host__ __device__ constexpr const Empty* begin() const { return nullptr; } __host__ __device__ constexpr const Empty* end() const { return &x; } }; __host__ __device__ constexpr bool testEndMember() { EndMember a{}; assert(cuda::std::ranges::end(a) == &a.x); assert(cuda::std::ranges::cend(a) == &a.x); NonConstEndMember b{}; assert(cuda::std::ranges::end(b) == &b.x); static_assert(!cuda::std::is_invocable_v, ""); EnabledBorrowingEndMember c{}; assert(cuda::std::ranges::end(cuda::std::move(c)) == &globalBuff[0]); assert(cuda::std::ranges::cend(cuda::std::move(c)) == &globalBuff[0]); EndMemberFunction d{}; assert(cuda::std::ranges::end(d) == &d.x); assert(cuda::std::ranges::cend(d) == &d.x); EmptyPtrEndMember e{}; assert(cuda::std::ranges::end(e) == &e.x); assert(cuda::std::ranges::cend(e) == &e.x); return true; } struct EndFunction { int x; __host__ __device__ friend constexpr const int* begin(EndFunction const&) { return nullptr; } __host__ __device__ friend constexpr const int* end(EndFunction 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 EndFunctionReturnsInt { __host__ __device__ friend constexpr int begin(EndFunctionReturnsInt const&); __host__ __device__ friend constexpr int end(EndFunctionReturnsInt const&); }; static_assert(!cuda::std::is_invocable_v, ""); struct EndFunctionReturnsVoidPtr { __host__ __device__ friend constexpr void* begin(EndFunctionReturnsVoidPtr const&); __host__ __device__ friend constexpr void* end(EndFunctionReturnsVoidPtr const&); }; static_assert(!cuda::std::is_invocable_v, ""); struct EndFunctionReturnsEmpty { __host__ __device__ friend constexpr Empty begin(EndFunctionReturnsEmpty const&); __host__ __device__ friend constexpr Empty end(EndFunctionReturnsEmpty const&); }; static_assert(!cuda::std::is_invocable_v, ""); struct EndFunctionReturnsPtrConvertible { __host__ __device__ friend constexpr PtrConvertible begin(EndFunctionReturnsPtrConvertible const&); __host__ __device__ friend constexpr PtrConvertible end(EndFunctionReturnsPtrConvertible const&); }; static_assert(!cuda::std::is_invocable_v, ""); struct NoBeginFunction { __host__ __device__ friend constexpr const int* end(NoBeginFunction const&); }; static_assert(!cuda::std::is_invocable_v, ""); struct EndFunctionByValue { __host__ __device__ friend constexpr int* begin(EndFunctionByValue) { return nullptr; } __host__ __device__ friend constexpr int* end(EndFunctionByValue) { return &globalBuff[1]; } }; static_assert(!cuda::std::is_invocable_v, ""); struct EndFunctionEnabledBorrowing { __host__ __device__ friend constexpr int* begin(EndFunctionEnabledBorrowing) { return nullptr; } __host__ __device__ friend constexpr int* end(EndFunctionEnabledBorrowing) { return &globalBuff[2]; } }; namespace cuda { namespace std { namespace ranges { template <> _CCCL_INLINE_VAR constexpr bool enable_borrowed_range = true; } } // namespace std } // namespace cuda struct EndFunctionReturnsEmptyPtr { Empty x; __host__ __device__ friend constexpr const Empty* begin(EndFunctionReturnsEmptyPtr const&) { return nullptr; } __host__ __device__ friend constexpr const Empty* end(EndFunctionReturnsEmptyPtr const& bf) { return &bf.x; } }; struct EndFunctionWithDataMember { int x; int end; __host__ __device__ friend constexpr const int* begin(EndFunctionWithDataMember const&) { return nullptr; } __host__ __device__ friend constexpr const int* end(EndFunctionWithDataMember const& bf) { return &bf.x; } }; struct EndFunctionWithPrivateEndMember { int y; __host__ __device__ friend constexpr const int* begin(EndFunctionWithPrivateEndMember const&) { return nullptr; } __host__ __device__ friend constexpr const int* end(EndFunctionWithPrivateEndMember const& bf) { return &bf.y; } private: __host__ __device__ const int* end() const; }; struct BeginMemberEndFunction { int x; __host__ __device__ constexpr const int* begin() const { return nullptr; } __host__ __device__ friend constexpr const int* end(BeginMemberEndFunction const& bf) { return &bf.x; } }; __host__ __device__ constexpr bool testEndFunction() { const EndFunction a{}; assert(cuda::std::ranges::end(a) == &a.x); assert(cuda::std::ranges::cend(a) == &a.x); EndFunction aa{}; static_assert(!cuda::std::is_invocable_v, ""); assert(cuda::std::ranges::cend(aa) == &aa.x); EndFunctionByValue b{}; assert(cuda::std::ranges::end(b) == &globalBuff[1]); assert(cuda::std::ranges::cend(b) == &globalBuff[1]); EndFunctionEnabledBorrowing c{}; assert(cuda::std::ranges::end(cuda::std::move(c)) == &globalBuff[2]); assert(cuda::std::ranges::cend(cuda::std::move(c)) == &globalBuff[2]); const EndFunctionReturnsEmptyPtr d{}; assert(cuda::std::ranges::end(d) == &d.x); assert(cuda::std::ranges::cend(d) == &d.x); EndFunctionReturnsEmptyPtr dd{}; static_assert(!cuda::std::is_invocable_v, ""); assert(cuda::std::ranges::cend(dd) == &dd.x); const EndFunctionWithDataMember e{}; assert(cuda::std::ranges::end(e) == &e.x); assert(cuda::std::ranges::cend(e) == &e.x); EndFunctionWithDataMember ee{}; static_assert(!cuda::std::is_invocable_v, ""); assert(cuda::std::ranges::cend(ee) == &ee.x); const EndFunctionWithPrivateEndMember f{}; assert(cuda::std::ranges::end(f) == &f.y); assert(cuda::std::ranges::cend(f) == &f.y); EndFunctionWithPrivateEndMember ff{}; static_assert(!cuda::std::is_invocable_v, ""); assert(cuda::std::ranges::cend(ff) == &ff.y); const BeginMemberEndFunction g{}; assert(cuda::std::ranges::end(g) == &g.x); assert(cuda::std::ranges::cend(g) == &g.x); BeginMemberEndFunction gg{}; static_assert(!cuda::std::is_invocable_v, ""); assert(cuda::std::ranges::cend(gg) == &gg.x); return true; } static_assert(noexcept(cuda::std::ranges::end(cuda::std::declval()))); static_assert(noexcept(cuda::std::ranges::cend(cuda::std::declval()))); // needs c++17's guaranteed copy elision #if !TEST_COMPILER(MSVC2019) // broken noexcept _CCCL_GLOBAL_CONSTANT struct NoThrowMemberEnd { __host__ __device__ ThrowingIterator begin() const; __host__ __device__ ThrowingIterator end() const noexcept; // auto(t.end()) doesn't throw } ntme; static_assert(noexcept(cuda::std::ranges::end(ntme)), ""); static_assert(noexcept(cuda::std::ranges::cend(ntme)), ""); _CCCL_GLOBAL_CONSTANT struct NoThrowADLEnd { __host__ __device__ ThrowingIterator begin() const; __host__ __device__ friend ThrowingIterator end(NoThrowADLEnd&) noexcept; // auto(end(t)) doesn't throw __host__ __device__ friend ThrowingIterator end(const NoThrowADLEnd&) noexcept; } ntae; static_assert(noexcept(cuda::std::ranges::end(ntae)), ""); static_assert(noexcept(cuda::std::ranges::cend(ntae)), ""); #endif // !TEST_COMPILER(MSVC2019) _CCCL_GLOBAL_CONSTANT struct NoThrowMemberEndReturnsRef { __host__ __device__ ThrowingIterator begin() const; __host__ __device__ ThrowingIterator& end() const noexcept; // auto(t.end()) may throw } ntmerr; static_assert(!noexcept(cuda::std::ranges::end(ntmerr)), ""); static_assert(!noexcept(cuda::std::ranges::cend(ntmerr)), ""); _CCCL_GLOBAL_CONSTANT struct EndReturnsArrayRef { __host__ __device__ auto begin() const noexcept -> int (&)[10]; __host__ __device__ auto end() const noexcept -> int (&)[10]; } erar; static_assert(noexcept(cuda::std::ranges::end(erar)), ""); static_assert(noexcept(cuda::std::ranges::cend(erar)), ""); #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(), ""); testEndMember(); static_assert(testEndMember(), ""); testEndFunction(); static_assert(testEndFunction(), ""); #if !TEST_COMPILER(MSVC2019) // broken noexcept unused(ntme); unused(ntae); #endif // !TEST_COMPILER(MSVC2019) unused(ntmerr); unused(erar); return 0; }