From 8cd246458ffed4894632c7e7ac566686288174f9 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Mon, 22 Jan 2024 09:55:41 -0800 Subject: [PATCH] Implement `ranges::{c}rend` (#1301) --- .../std/detail/libcxx/include/CMakeLists.txt | 1 + .../std/detail/libcxx/include/__ranges/rend.h | 191 ++++++ .../cuda/std/detail/libcxx/include/ranges | 1 + .../std/ranges/range.access/rend.pass.cpp | 549 +++++++++++++++++ .../std/ranges/range.access/rend.verify.cpp | 26 + .../std/ranges/range.access/rend.pass.cpp | 576 ++++++++++++++++++ .../std/ranges/range.access/rend.verify.cpp | 34 ++ 7 files changed, 1378 insertions(+) create mode 100644 libcudacxx/include/cuda/std/detail/libcxx/include/__ranges/rend.h create mode 100644 libcudacxx/libcxx/test/std/ranges/range.access/rend.pass.cpp create mode 100644 libcudacxx/libcxx/test/std/ranges/range.access/rend.verify.cpp create mode 100644 libcudacxx/test/libcudacxx/std/ranges/range.access/rend.pass.cpp create mode 100644 libcudacxx/test/libcudacxx/std/ranges/range.access/rend.verify.cpp diff --git a/libcudacxx/include/cuda/std/detail/libcxx/include/CMakeLists.txt b/libcudacxx/include/cuda/std/detail/libcxx/include/CMakeLists.txt index 24e3576ddb..6ebe5d85bb 100644 --- a/libcudacxx/include/cuda/std/detail/libcxx/include/CMakeLists.txt +++ b/libcudacxx/include/cuda/std/detail/libcxx/include/CMakeLists.txt @@ -190,6 +190,7 @@ set(files __ranges/enable_borrowed_range.h __ranges/enable_view.h __ranges/rbegin.h + __ranges/rend.h __split_buffer __sso_allocator __std_stream diff --git a/libcudacxx/include/cuda/std/detail/libcxx/include/__ranges/rend.h b/libcudacxx/include/cuda/std/detail/libcxx/include/__ranges/rend.h new file mode 100644 index 0000000000..2f64d42efa --- /dev/null +++ b/libcudacxx/include/cuda/std/detail/libcxx/include/__ranges/rend.h @@ -0,0 +1,191 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// 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. +// +//===----------------------------------------------------------------------===// +#ifndef _LIBCUDACXX___RANGES_REND_H +#define _LIBCUDACXX___RANGES_REND_H + +#ifndef __cuda_std__ +#include <__config> +#endif // __cuda_std__ + +#if defined(_CCCL_IMPLICIT_SYSTEM_HEADER_GCC) +# pragma GCC system_header +#elif defined(_CCCL_IMPLICIT_SYSTEM_HEADER_CLANG) +# pragma clang system_header +#elif defined(_CCCL_IMPLICIT_SYSTEM_HEADER_MSVC) +# pragma system_header +#endif // no system header + +#include "../__concepts/class_or_enum.h" +#include "../__concepts/same_as.h" +#include "../__iterator/concepts.h" +#include "../__iterator/readable_traits.h" +#include "../__iterator/reverse_iterator.h" +#include "../__ranges/access.h" +#include "../__ranges/rbegin.h" +#include "../__type_traits/is_reference.h" +#include "../__type_traits/remove_cvref.h" +#include "../__type_traits/remove_reference.h" +#include "../__utility/auto_cast.h" + +_LIBCUDACXX_BEGIN_NAMESPACE_RANGES + +#if _CCCL_STD_VER >= 2017 && !defined(_LIBCUDACXX_COMPILER_MSVC_2017) + +// [range.access.rend] + +_LIBCUDACXX_BEGIN_NAMESPACE_CPO(__rend) + template + void rend(_Tp&) = delete; + template + void rend(const _Tp&) = delete; + +#if _CCCL_STD_VER >= 2020 + template + concept __member_rend = + __can_borrow<_Tp> && + __workaround_52970<_Tp> && + requires(_Tp&& __t) { + _CUDA_VRANGES::rbegin(__t); + { _LIBCUDACXX_AUTO_CAST(__t.rend()) } -> sentinel_for; + }; + + + template + concept __unqualified_rend = + !__member_rend<_Tp> && + __can_borrow<_Tp> && + __class_or_enum> && + requires(_Tp&& __t) { + _CUDA_VRANGES::rbegin(__t); + { _LIBCUDACXX_AUTO_CAST(rend(__t)) } -> sentinel_for; + }; + + template + concept __can_reverse = + __can_borrow<_Tp> && + !__member_rend<_Tp> && + !__unqualified_rend<_Tp> && + requires(_Tp&& __t) { + { _CUDA_VRANGES::begin(__t) } -> same_as; + { _CUDA_VRANGES::begin(__t) } -> bidirectional_iterator; + }; +#else // ^^^ CXX20 ^^^ / vvv CXX17 vvv + template + _LIBCUDACXX_CONCEPT_FRAGMENT( + __member_rend_, + requires(_Tp&& __t)( + requires(__can_borrow<_Tp>), + requires(__workaround_52970<_Tp>), + typename(decltype(_CUDA_VRANGES::rbegin(__t))), + requires(sentinel_for) + )); + + template + _LIBCUDACXX_CONCEPT __member_rend = _LIBCUDACXX_FRAGMENT(__member_rend_, _Tp); + + template + _LIBCUDACXX_CONCEPT_FRAGMENT( + __unqualified_rend_, + requires(_Tp&& __t)( + requires(!__member_rend<_Tp>), + requires(__can_borrow<_Tp>), + requires(__class_or_enum>), + typename(decltype(_CUDA_VRANGES::rbegin(__t))), + requires(sentinel_for) + )); + + template + _LIBCUDACXX_CONCEPT __unqualified_rend = _LIBCUDACXX_FRAGMENT(__unqualified_rend_, _Tp); + + template + _LIBCUDACXX_CONCEPT_FRAGMENT( + __can_reverse_, + requires(_Tp&& __t)( + requires(!__member_rend<_Tp>), + requires(!__unqualified_rend<_Tp>), + requires(__can_borrow<_Tp>), + requires(same_as), + requires(bidirectional_iterator) + )); + + template + _LIBCUDACXX_CONCEPT __can_reverse = _LIBCUDACXX_FRAGMENT(__can_reverse_, _Tp); +#endif // _CCCL_STD_VER <= 2017 + + class __fn { + public: + _LIBCUDACXX_TEMPLATE(class _Tp) + _LIBCUDACXX_REQUIRES(__member_rend<_Tp>) + _LIBCUDACXX_NODISCARD_ATTRIBUTE _LIBCUDACXX_HIDE_FROM_ABI _LIBCUDACXX_INLINE_VISIBILITY + constexpr auto operator()(_Tp&& __t) const + noexcept(noexcept(_LIBCUDACXX_AUTO_CAST(__t.rend()))) + { + return _LIBCUDACXX_AUTO_CAST(__t.rend()); + } + + _LIBCUDACXX_TEMPLATE(class _Tp) + _LIBCUDACXX_REQUIRES(__unqualified_rend<_Tp>) + _LIBCUDACXX_NODISCARD_ATTRIBUTE _LIBCUDACXX_HIDE_FROM_ABI _LIBCUDACXX_INLINE_VISIBILITY + constexpr auto operator()(_Tp&& __t) const + noexcept(noexcept(_LIBCUDACXX_AUTO_CAST(rend(__t)))) + { + return _LIBCUDACXX_AUTO_CAST(rend(__t)); + } + + _LIBCUDACXX_TEMPLATE(class _Tp) + _LIBCUDACXX_REQUIRES(__can_reverse<_Tp>) + _LIBCUDACXX_NODISCARD_ATTRIBUTE _LIBCUDACXX_HIDE_FROM_ABI _LIBCUDACXX_INLINE_VISIBILITY + constexpr auto operator()(_Tp&& __t) const + noexcept(noexcept(_CUDA_VRANGES::begin(__t))) + { + return _CUDA_VSTD::make_reverse_iterator(_CUDA_VRANGES::begin(__t)); + } + + _LIBCUDACXX_TEMPLATE(class _Tp) + _LIBCUDACXX_REQUIRES((!__member_rend<_Tp> && !__unqualified_rend<_Tp> && !__can_reverse<_Tp>)) + void operator()(_Tp&&) const = delete; + }; +_LIBCUDACXX_END_NAMESPACE_CPO + +inline namespace __cpo { + _LIBCUDACXX_CPO_ACCESSIBILITY auto rend = __rend::__fn{}; +} // namespace __cpo + +// [range.access.crend] + +_LIBCUDACXX_BEGIN_NAMESPACE_CPO(__crend) + struct __fn { + _LIBCUDACXX_TEMPLATE(class _Tp) + _LIBCUDACXX_REQUIRES(is_lvalue_reference_v<_Tp&&>) + _LIBCUDACXX_NODISCARD_ATTRIBUTE _LIBCUDACXX_HIDE_FROM_ABI _LIBCUDACXX_INLINE_VISIBILITY + constexpr auto operator()(_Tp&& __t) const + noexcept(noexcept(_CUDA_VRANGES::rend(static_cast&>(__t)))) + -> decltype( _CUDA_VRANGES::rend(static_cast&>(__t))) + { return _CUDA_VRANGES::rend(static_cast&>(__t)); } + + _LIBCUDACXX_TEMPLATE(class _Tp) + _LIBCUDACXX_REQUIRES(is_rvalue_reference_v<_Tp&&>) + _LIBCUDACXX_NODISCARD_ATTRIBUTE _LIBCUDACXX_HIDE_FROM_ABI _LIBCUDACXX_INLINE_VISIBILITY + constexpr auto operator()(_Tp&& __t) const + noexcept(noexcept(_CUDA_VRANGES::rend(static_cast(__t)))) + -> decltype( _CUDA_VRANGES::rend(static_cast(__t))) + { return _CUDA_VRANGES::rend(static_cast(__t)); } + }; +_LIBCUDACXX_END_NAMESPACE_CPO + +inline namespace __cpo { + _LIBCUDACXX_CPO_ACCESSIBILITY auto crend = __crend::__fn{}; +} // namespace __cpo + +#endif // _CCCL_STD_VER >= 2017 && !_LIBCUDACXX_COMPILER_MSVC_2017 + +_LIBCUDACXX_END_NAMESPACE_RANGES + +#endif // _LIBCUDACXX___RANGES_REND_H diff --git a/libcudacxx/include/cuda/std/detail/libcxx/include/ranges b/libcudacxx/include/cuda/std/detail/libcxx/include/ranges index 92d99e5a79..88da511d0c 100644 --- a/libcudacxx/include/cuda/std/detail/libcxx/include/ranges +++ b/libcudacxx/include/cuda/std/detail/libcxx/include/ranges @@ -315,6 +315,7 @@ namespace std { #include "__ranges/enable_borrowed_range.h" #include "__ranges/enable_view.h" #include "__ranges/rbegin.h" +#include "__ranges/rend.h" // standard-mandated includes #include "version" diff --git a/libcudacxx/libcxx/test/std/ranges/range.access/rend.pass.cpp b/libcudacxx/libcxx/test/std/ranges/range.access/rend.pass.cpp new file mode 100644 index 0000000000..5ba244b6b1 --- /dev/null +++ b/libcudacxx/libcxx/test/std/ranges/range.access/rend.pass.cpp @@ -0,0 +1,549 @@ +//===----------------------------------------------------------------------===// +// +// 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; +} diff --git a/libcudacxx/libcxx/test/std/ranges/range.access/rend.verify.cpp b/libcudacxx/libcxx/test/std/ranges/range.access/rend.verify.cpp new file mode 100644 index 0000000000..0c57ba23e0 --- /dev/null +++ b/libcudacxx/libcxx/test/std/ranges/range.access/rend.verify.cpp @@ -0,0 +1,26 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +#include + +struct NonBorrowedRange { + int* begin() const; + int* end() const; +}; +static_assert(!std::ranges::enable_borrowed_range); + +// Verify that if the expression is an rvalue and `enable_borrowed_range` is false, `ranges::rend` is ill-formed. +void test() { + std::ranges::rend(NonBorrowedRange()); + // expected-error-re@-1 {{{{call to deleted function call operator in type 'const (std::ranges::)?__rend::__fn'}}}} + // expected-error@-2 {{attempt to use a deleted function}} +} diff --git a/libcudacxx/test/libcudacxx/std/ranges/range.access/rend.pass.cpp b/libcudacxx/test/libcudacxx/std/ranges/range.access/rend.pass.cpp new file mode 100644 index 0000000000..5392931343 --- /dev/null +++ b/libcudacxx/test/libcudacxx/std/ranges/range.access/rend.pass.cpp @@ -0,0 +1,576 @@ +//===----------------------------------------------------------------------===// +// +// 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: c++03, c++11, c++14 +// UNSUPPORTED: msvc-19.16 + +// cuda::std::ranges::rend +// cuda::std::ranges::crend + +#include + +#include +#include + +#include "test_macros.h" +#include "test_iterators.h" + +using RangeREndT = decltype(cuda::std::ranges::rend); +using RangeCREndT = decltype(cuda::std::ranges::crend); + +STATIC_TEST_GLOBAL_VAR 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); + ASSERT_SAME_TYPE(decltype(cuda::std::ranges::rend(x)), cuda::std::reverse_iterator); + ASSERT_SAME_TYPE(decltype(cuda::std::ranges::crend(x)), cuda::std::reverse_iterator); + } + + { + int x[2][2] = {}; + unused(x); + ASSERT_SAME_TYPE(decltype(cuda::std::ranges::rend(x)), cuda::std::reverse_iterator); + ASSERT_SAME_TYPE(decltype(cuda::std::ranges::crend(x)), cuda::std::reverse_iterator); + } + + { + Different x{}; + unused(x); + ASSERT_SAME_TYPE(decltype(cuda::std::ranges::rend(x)), sentinel_wrapper); + ASSERT_SAME_TYPE(decltype(cuda::std::ranges::crend(x)), sentinel_wrapper); + } + + return true; +} + +__host__ __device__ TEST_CONSTEXPR_CXX17 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__ TEST_CONSTEXPR_CXX17 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; +} +ASSERT_NOEXCEPT(cuda::std::ranges::rend(cuda::std::declval())); +ASSERT_NOEXCEPT(cuda::std::ranges::crend(cuda::std::declval())); + +#if !defined(TEST_COMPILER_MSVC_2019) +_LIBCUDACXX_CPO_ACCESSIBILITY 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))); + +_LIBCUDACXX_CPO_ACCESSIBILITY 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_MSVC_2019 + +#if !defined(TEST_COMPILER_ICC) +_LIBCUDACXX_CPO_ACCESSIBILITY 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))); +#endif // !TEST_COMPILER_ICC + +_LIBCUDACXX_CPO_ACCESSIBILITY 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))); + +_LIBCUDACXX_CPO_ACCESSIBILITY 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))); + +#if !defined(TEST_COMPILER_ICC) +_LIBCUDACXX_CPO_ACCESSIBILITY 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))); +#endif // !TEST_COMPILER_ICC + +#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(); +#ifndef TEST_COMPILER_CUDACC_BELOW_11_3 + static_assert(testArray()); +#endif // TEST_COMPILER_CUDACC_BELOW_11_3 + + testREndMember(); + static_assert(testREndMember()); + + testREndFunction(); + static_assert(testREndFunction()); + + testBeginEnd(); + static_assert(testBeginEnd()); + +#if !defined(TEST_COMPILER_MSVC_2019) + unused(ntmre); + unused(ntare); +#endif // !TEST_COMPILER_MSVC_2019 +#if !defined(TEST_COMPILER_ICC) + unused(ntmrerr); +#endif // !TEST_COMPILER_ICC + unused(rerar); + unused(ntbte); +#if !defined(TEST_COMPILER_ICC) + unused(ntetb); +#endif // !TEST_COMPILER_ICC + + return 0; +} diff --git a/libcudacxx/test/libcudacxx/std/ranges/range.access/rend.verify.cpp b/libcudacxx/test/libcudacxx/std/ranges/range.access/rend.verify.cpp new file mode 100644 index 0000000000..66d7bf2b0d --- /dev/null +++ b/libcudacxx/test/libcudacxx/std/ranges/range.access/rend.verify.cpp @@ -0,0 +1,34 @@ +//===----------------------------------------------------------------------===// +// +// 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: c++03, c++11, c++14 +// UNSUPPORTED: nvrtc +// UNSUPPORTED: msvc-19.16 + +// cuda::std::ranges::rend + +#include + +struct NonBorrowedRange { + __host__ __device__ int* begin() const; + __host__ __device__ int* end() const; +}; +static_assert(!cuda::std::ranges::enable_borrowed_range); + +// Verify that if the expression is an rvalue and `enable_borrowed_range` is false, `ranges::rend` is ill-formed. +__host__ __device__ void test() { + cuda::std::ranges::rend(NonBorrowedRange()); + // expected-error-re@-1 {{{{call to deleted function call operator in type 'const (cuda::std::ranges::)?__rend::__fn'}}}} + // expected-error@-2 {{attempt to use a deleted function}} +} + +int main(int, char**) +{ + return 0; +}