From e599d5e6a53ae14723353811ade73cb8a1c84728 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Wed, 13 Dec 2023 08:45:08 +0100 Subject: [PATCH] Implement `ranges::{c}begin` and `ranges::{c}end` --- .../std/detail/libcxx/include/CMakeLists.txt | 1 + .../detail/libcxx/include/__ranges/access.h | 297 +++++++++++++ .../libcxx/include/__utility/auto_cast.h | 4 + .../cuda/std/detail/libcxx/include/ranges | 1 + .../std/ranges/range.access/begin.pass.cpp | 347 ++++++++++++++++ .../range.access/begin.sizezero.pass.cpp | 35 ++ .../std/ranges/range.access/begin.verify.cpp | 26 ++ .../test/std/ranges/range.access/end.pass.cpp | 370 +++++++++++++++++ .../ranges/range.access/end.sizezero.pass.cpp | 35 ++ .../std/ranges/range.access/end.verify.cpp | 26 ++ .../std/ranges/range.access/begin.pass.cpp | 370 +++++++++++++++++ .../range.access/begin.sizezero.pass.cpp | 43 ++ .../std/ranges/range.access/begin.verify.cpp | 33 ++ .../std/ranges/range.access/end.pass.cpp | 391 ++++++++++++++++++ .../ranges/range.access/end.sizezero.pass.cpp | 43 ++ .../std/ranges/range.access/end.verify.cpp | 33 ++ 16 files changed, 2055 insertions(+) create mode 100644 libcudacxx/include/cuda/std/detail/libcxx/include/__ranges/access.h create mode 100644 libcudacxx/libcxx/test/std/ranges/range.access/begin.pass.cpp create mode 100644 libcudacxx/libcxx/test/std/ranges/range.access/begin.sizezero.pass.cpp create mode 100644 libcudacxx/libcxx/test/std/ranges/range.access/begin.verify.cpp create mode 100644 libcudacxx/libcxx/test/std/ranges/range.access/end.pass.cpp create mode 100644 libcudacxx/libcxx/test/std/ranges/range.access/end.sizezero.pass.cpp create mode 100644 libcudacxx/libcxx/test/std/ranges/range.access/end.verify.cpp create mode 100644 libcudacxx/test/libcudacxx/std/ranges/range.access/begin.pass.cpp create mode 100644 libcudacxx/test/libcudacxx/std/ranges/range.access/begin.sizezero.pass.cpp create mode 100644 libcudacxx/test/libcudacxx/std/ranges/range.access/begin.verify.cpp create mode 100644 libcudacxx/test/libcudacxx/std/ranges/range.access/end.pass.cpp create mode 100644 libcudacxx/test/libcudacxx/std/ranges/range.access/end.sizezero.pass.cpp create mode 100644 libcudacxx/test/libcudacxx/std/ranges/range.access/end.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 00ddbcb637e..d9c8d47728a 100644 --- a/libcudacxx/include/cuda/std/detail/libcxx/include/CMakeLists.txt +++ b/libcudacxx/include/cuda/std/detail/libcxx/include/CMakeLists.txt @@ -186,6 +186,7 @@ set(files __mutex_base __node_handle __nullptr + __ranges/access.h __ranges/enable_borrowed_range.h __ranges/enable_view.h __split_buffer diff --git a/libcudacxx/include/cuda/std/detail/libcxx/include/__ranges/access.h b/libcudacxx/include/cuda/std/detail/libcxx/include/__ranges/access.h new file mode 100644 index 00000000000..5f36fdbb0bd --- /dev/null +++ b/libcudacxx/include/cuda/std/detail/libcxx/include/__ranges/access.h @@ -0,0 +1,297 @@ +// -*- 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_ACCESS_H +#define _LIBCUDACXX___RANGES_ACCESS_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 "../__iterator/concepts.h" +#include "../__iterator/readable_traits.h" +#include "../__ranges/enable_borrowed_range.h" +#include "../__type_traits/is_array.h" +#include "../__type_traits/is_reference.h" +#include "../__type_traits/remove_cvref.h" +#include "../__type_traits/remove_reference.h" +#include "../__utility/auto_cast.h" +#include "../__utility/declval.h" + +_LIBCUDACXX_BEGIN_NAMESPACE_RANGES + +#if _CCCL_STD_VER > 2014 && !defined(_LIBCUDACXX_COMPILER_MSVC_2017) + + template + _LIBCUDACXX_CONCEPT __can_borrow = + is_lvalue_reference_v<_Tp> || enable_borrowed_range>; + +// [range.access.begin] + +_LIBCUDACXX_BEGIN_NAMESPACE_CPO(__begin) + template + void begin(_Tp&) = delete; + template + void begin(const _Tp&) = delete; + +#if _CCCL_STD_VER > 2017 + template + concept __member_begin = + __can_borrow<_Tp> && + __workaround_52970<_Tp> && + requires(_Tp&& __t) { + { _LIBCUDACXX_AUTO_CAST(__t.begin()) } -> input_or_output_iterator; + }; + + template + concept __unqualified_begin = + !__member_begin<_Tp> && + __can_borrow<_Tp> && + __class_or_enum> && + requires(_Tp && __t) { + { _LIBCUDACXX_AUTO_CAST(begin(__t)) } -> input_or_output_iterator; + }; +#else // ^^^ CXX20 ^^^ / vvv CXX17 vvv + template + _LIBCUDACXX_CONCEPT_FRAGMENT( + __member_begin_, + requires(_Tp&& __t)( + requires(__can_borrow<_Tp>), + requires(__workaround_52970<_Tp>), + requires(input_or_output_iterator) + )); + + template + _LIBCUDACXX_CONCEPT __member_begin = _LIBCUDACXX_FRAGMENT(__member_begin_, _Tp); + + template + _LIBCUDACXX_CONCEPT_FRAGMENT( + __unqualified_begin_, + requires(_Tp&& __t)( + requires(!__member_begin<_Tp>), + requires(__can_borrow<_Tp>), + requires(__class_or_enum>), + requires(input_or_output_iterator) + )); + + template + _LIBCUDACXX_CONCEPT __unqualified_begin = _LIBCUDACXX_FRAGMENT(__unqualified_begin_, _Tp); +#endif // _CCCL_STD_VER < 2020 + + struct __fn { + // This has been made valid as a defect report for C++17 onwards, however gcc below 11.0 does not implement it +#if (!defined(_LIBCUDACXX_COMPILER_GCC) || __GNUC__ >= 11) + _LIBCUDACXX_TEMPLATE(class _Tp) + _LIBCUDACXX_REQUIRES((sizeof(_Tp) >= 0)) // Disallow incomplete element types. + _LIBCUDACXX_NODISCARD_ATTRIBUTE _LIBCUDACXX_HIDE_FROM_ABI _LIBCUDACXX_INLINE_VISIBILITY + constexpr auto operator()(_Tp (&__t)[]) const noexcept + { + return __t + 0; + } +#endif // (!defined(__GNUC__) || __GNUC__ >= 11) + + _LIBCUDACXX_TEMPLATE(class _Tp, size_t _Np) + _LIBCUDACXX_REQUIRES((sizeof(_Tp) >= 0)) // Disallow incomplete element types. + _LIBCUDACXX_NODISCARD_ATTRIBUTE _LIBCUDACXX_HIDE_FROM_ABI _LIBCUDACXX_INLINE_VISIBILITY + constexpr auto operator()(_Tp (&__t)[_Np]) const noexcept + { + return __t + 0; + } + + _LIBCUDACXX_TEMPLATE(class _Tp) + _LIBCUDACXX_REQUIRES(__member_begin<_Tp>) + _LIBCUDACXX_NODISCARD_ATTRIBUTE _LIBCUDACXX_HIDE_FROM_ABI _LIBCUDACXX_INLINE_VISIBILITY + constexpr auto operator()(_Tp&& __t) const + noexcept(noexcept(_LIBCUDACXX_AUTO_CAST(__t.begin()))) + { + return _LIBCUDACXX_AUTO_CAST(__t.begin()); + } + + _LIBCUDACXX_TEMPLATE(class _Tp) + _LIBCUDACXX_REQUIRES(__unqualified_begin<_Tp>) + _LIBCUDACXX_NODISCARD_ATTRIBUTE _LIBCUDACXX_HIDE_FROM_ABI _LIBCUDACXX_INLINE_VISIBILITY + constexpr auto operator()(_Tp&& __t) const + noexcept(noexcept(_LIBCUDACXX_AUTO_CAST(begin(__t)))) + { + return _LIBCUDACXX_AUTO_CAST(begin(__t)); + } + + _LIBCUDACXX_TEMPLATE(class _Tp) + _LIBCUDACXX_REQUIRES((!__member_begin<_Tp>) _LIBCUDACXX_AND + (!__unqualified_begin<_Tp>)) + void operator()(_Tp&&) const = delete; + }; +_LIBCUDACXX_END_NAMESPACE_CPO + +inline namespace __cpo { + _LIBCUDACXX_CPO_ACCESSIBILITY auto begin = __begin::__fn{}; +} // namespace __cpo + +// [range.range] + + template + using iterator_t = decltype(_CUDA_VRANGES::begin(_CUDA_VSTD::declval<_Tp&>())); + +// [range.access.end] + +_LIBCUDACXX_BEGIN_NAMESPACE_CPO(__end) + template + void end(_Tp&) = delete; + template + void end(const _Tp&) = delete; + +#if _CCCL_STD_VER > 2017 + template + concept __member_end = + __can_borrow<_Tp> && + __workaround_52970<_Tp> && + requires(_Tp&& __t) { + typename iterator_t<_Tp>; + { _LIBCUDACXX_AUTO_CAST(__t.end()) } -> sentinel_for>; + }; + + template + concept __unqualified_end = + !__member_end<_Tp> && + __can_borrow<_Tp> && + __class_or_enum> && + requires(_Tp && __t) { + typename iterator_t<_Tp>; + { _LIBCUDACXX_AUTO_CAST(end(__t)) } -> sentinel_for>; + }; +#else // ^^^ CXX20 ^^^ / vvv CXX17 vvv +template + _LIBCUDACXX_CONCEPT_FRAGMENT( + __member_end_, + requires(_Tp&& __t)( + requires(__can_borrow<_Tp>), + requires(__workaround_52970<_Tp>), + typename(iterator_t<_Tp>), + requires(sentinel_for>) + )); + + template + _LIBCUDACXX_CONCEPT __member_end = _LIBCUDACXX_FRAGMENT(__member_end_, _Tp); + + template + _LIBCUDACXX_CONCEPT_FRAGMENT( + __unqualified_end_, + requires(_Tp&& __t)( + requires(!__member_end<_Tp>), + requires(__can_borrow<_Tp>), + requires(__class_or_enum>), + typename(iterator_t<_Tp>), + requires(sentinel_for>) + )); + + template + _LIBCUDACXX_CONCEPT __unqualified_end = _LIBCUDACXX_FRAGMENT(__unqualified_end_, _Tp); +#endif // _CCCL_STD_VER < 2020 + + struct __fn { + _LIBCUDACXX_TEMPLATE(class _Tp, size_t _Np) + _LIBCUDACXX_REQUIRES((sizeof(_Tp) >= 0)) // Disallow incomplete element types. + _LIBCUDACXX_NODISCARD_ATTRIBUTE _LIBCUDACXX_HIDE_FROM_ABI _LIBCUDACXX_INLINE_VISIBILITY + constexpr auto operator()(_Tp (&__t)[_Np]) const noexcept + { + return __t + _Np; + } + + _LIBCUDACXX_TEMPLATE(class _Tp) + _LIBCUDACXX_REQUIRES(__member_end<_Tp>) + _LIBCUDACXX_NODISCARD_ATTRIBUTE _LIBCUDACXX_HIDE_FROM_ABI _LIBCUDACXX_INLINE_VISIBILITY + constexpr auto operator()(_Tp&& __t) const noexcept(noexcept(_LIBCUDACXX_AUTO_CAST(__t.end()))) + { + return _LIBCUDACXX_AUTO_CAST(__t.end()); + } + + _LIBCUDACXX_TEMPLATE(class _Tp) + _LIBCUDACXX_REQUIRES(__unqualified_end<_Tp>) + _LIBCUDACXX_NODISCARD_ATTRIBUTE _LIBCUDACXX_HIDE_FROM_ABI _LIBCUDACXX_INLINE_VISIBILITY + constexpr auto operator()(_Tp&& __t) const noexcept(noexcept(_LIBCUDACXX_AUTO_CAST(end(__t)))) + { + return _LIBCUDACXX_AUTO_CAST(end(__t)); + } + + _LIBCUDACXX_TEMPLATE(class _Tp) + _LIBCUDACXX_REQUIRES((!__member_end<_Tp>) _LIBCUDACXX_AND + (!__unqualified_end<_Tp>)) + void operator()(_Tp&&) const = delete; + }; +_LIBCUDACXX_END_NAMESPACE_CPO + +inline namespace __cpo { + _LIBCUDACXX_CPO_ACCESSIBILITY auto end = __end::__fn{}; +} // namespace __cpo + +// [range.access.cbegin] + +_LIBCUDACXX_BEGIN_NAMESPACE_CPO(__cbegin) + 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::begin(static_cast&>(__t)))) + -> decltype( _CUDA_VRANGES::begin(static_cast&>(__t))) + { return _CUDA_VRANGES::begin(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::begin(static_cast(__t)))) + -> decltype( _CUDA_VRANGES::begin(static_cast(__t))) + { return _CUDA_VRANGES::begin(static_cast(__t)); } + }; +_LIBCUDACXX_END_NAMESPACE_CPO + +inline namespace __cpo { + _LIBCUDACXX_CPO_ACCESSIBILITY auto cbegin = __cbegin::__fn{}; +} // namespace __cpo + +// [range.access.cend] + +_LIBCUDACXX_BEGIN_NAMESPACE_CPO(__cend) + 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::end(static_cast&>(__t)))) + -> decltype( _CUDA_VRANGES::end(static_cast&>(__t))) + { return _CUDA_VRANGES::end(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::end(static_cast(__t)))) + -> decltype( _CUDA_VRANGES::end(static_cast(__t))) + { return _CUDA_VRANGES::end(static_cast(__t)); } + }; +_LIBCUDACXX_END_NAMESPACE_CPO + +inline namespace __cpo { + _LIBCUDACXX_CPO_ACCESSIBILITY auto cend = __cend::__fn{}; +} // namespace __cpo +#endif // _CCCL_STD_VER > 2014 && !_LIBCUDACXX_COMPILER_MSVC_2017 + +_LIBCUDACXX_END_NAMESPACE_RANGES + +#endif // _LIBCUDACXX___RANGES_ACCESS_H diff --git a/libcudacxx/include/cuda/std/detail/libcxx/include/__utility/auto_cast.h b/libcudacxx/include/cuda/std/detail/libcxx/include/__utility/auto_cast.h index 82ab6acc5e8..bb2de230c83 100644 --- a/libcudacxx/include/cuda/std/detail/libcxx/include/__utility/auto_cast.h +++ b/libcudacxx/include/cuda/std/detail/libcxx/include/__utility/auto_cast.h @@ -25,6 +25,10 @@ # pragma system_header #endif // no system header +#if _CCCL_STD_VER < 2020 && defined(_LIBCUDACXX_COMPILER_MSVC) +#define _LIBCUDACXX_AUTO_CAST(expr) (_CUDA_VSTD::decay_t)(expr) +#else #define _LIBCUDACXX_AUTO_CAST(expr) static_cast<_CUDA_VSTD::decay_t>(expr) +#endif #endif // _LIBCUDACXX___UTILITY_AUTO_CAST_H diff --git a/libcudacxx/include/cuda/std/detail/libcxx/include/ranges b/libcudacxx/include/cuda/std/detail/libcxx/include/ranges index 5ffe0d4f5fc..87c4eb320ec 100644 --- a/libcudacxx/include/cuda/std/detail/libcxx/include/ranges +++ b/libcudacxx/include/cuda/std/detail/libcxx/include/ranges @@ -311,6 +311,7 @@ namespace std { #endif // __cuda_std__ #include "__assert" // all public C++ headers provide the assertion handler +#include "__ranges/access.h" #include "__ranges/enable_borrowed_range.h" #include "__ranges/enable_view.h" diff --git a/libcudacxx/libcxx/test/std/ranges/range.access/begin.pass.cpp b/libcudacxx/libcxx/test/std/ranges/range.access/begin.pass.cpp new file mode 100644 index 00000000000..6cb2370fbc1 --- /dev/null +++ b/libcudacxx/libcxx/test/std/ranges/range.access/begin.pass.cpp @@ -0,0 +1,347 @@ +//===----------------------------------------------------------------------===// +// +// 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::begin +// std::ranges::cbegin + +#include + +#include +#include +#include "test_macros.h" +#include "test_iterators.h" + +using RangeBeginT = decltype(std::ranges::begin); +using RangeCBeginT = decltype(std::ranges::cbegin); + +static int globalBuff[8]; + +static_assert(!std::is_invocable_v); +static_assert( std::is_invocable_v); +static_assert(!std::is_invocable_v); +// This has been made valid as a defect report for C++17 onwards, however both clang and gcc below 11.0 does not implement it +#if (!defined(TEST_COMPILER_GCC) || __GNUC__ >= 11) +static_assert( std::is_invocable_v); +#endif // (!defined(TEST_COMPILER_GCC) || __GNUC__ >= 11) +static_assert(!std::is_invocable_v); +static_assert( std::is_invocable_v); +static_assert(!std::is_invocable_v); +// This has been made valid as a defect report for C++17 onwards, however both clang and gcc below 11.0 does not implement it +#if (!defined(TEST_COMPILER_GCC) || __GNUC__ >= 11) +static_assert( std::is_invocable_v); +#endif // (!defined(TEST_COMPILER_GCC) || __GNUC__ >= 11) + +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 BeginMember { + int x; + constexpr const int *begin() 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::begin(x)), int**); + ASSERT_SAME_TYPE(decltype(std::ranges::cbegin(x)), int* const*); + } + { + int x[2][2]; + ASSERT_SAME_TYPE(decltype(std::ranges::begin(x)), int(*)[2]); + ASSERT_SAME_TYPE(decltype(std::ranges::cbegin(x)), const int(*)[2]); + } + { + struct Different { + char*& begin(); + short*& begin() const; + } x; + ASSERT_SAME_TYPE(decltype(std::ranges::begin(x)), char*); + ASSERT_SAME_TYPE(decltype(std::ranges::cbegin(x)), short*); + } + return true; +} + +constexpr bool testArray() { + int a[2]; + assert(std::ranges::begin(a) == a); + assert(std::ranges::cbegin(a) == a); + + int b[2][2]; + assert(std::ranges::begin(b) == b); + assert(std::ranges::cbegin(b) == b); + + BeginMember c[2]; + assert(std::ranges::begin(c) == c); + assert(std::ranges::cbegin(c) == c); + + return true; +} + +struct BeginMemberReturnsInt { + int begin() const; +}; +static_assert(!std::is_invocable_v); + +struct BeginMemberReturnsVoidPtr { + const void *begin() const; +}; +static_assert(!std::is_invocable_v); + +struct EmptyBeginMember { + struct iterator {}; + iterator begin() const; +}; +static_assert(!std::is_invocable_v); + +struct PtrConvertibleBeginMember { + struct iterator { operator int*() const; }; + iterator begin() const; +}; +static_assert(!std::is_invocable_v); + +struct NonConstBeginMember { + int x; + constexpr int *begin() { 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 EnabledBorrowingBeginMember { + constexpr int *begin() const { return &globalBuff[0]; } +}; +template<> +inline constexpr bool std::ranges::enable_borrowed_range = true; + +struct BeginMemberFunction { + int x; + constexpr const int *begin() const { return &x; } + friend int *begin(BeginMemberFunction const&); +}; + +struct EmptyPtrBeginMember { + struct Empty {}; + Empty x; + constexpr const Empty *begin() const { return &x; } +}; + +constexpr bool testBeginMember() { + BeginMember a; + assert(std::ranges::begin(a) == &a.x); + assert(std::ranges::cbegin(a) == &a.x); + static_assert(!std::is_invocable_v); + static_assert(!std::is_invocable_v); + + NonConstBeginMember b; + assert(std::ranges::begin(b) == &b.x); + static_assert(!std::is_invocable_v); + + EnabledBorrowingBeginMember c; + assert(std::ranges::begin(c) == &globalBuff[0]); + assert(std::ranges::cbegin(c) == &globalBuff[0]); + assert(std::ranges::begin(std::move(c)) == &globalBuff[0]); + assert(std::ranges::cbegin(std::move(c)) == &globalBuff[0]); + + BeginMemberFunction d; + assert(std::ranges::begin(d) == &d.x); + assert(std::ranges::cbegin(d) == &d.x); + + EmptyPtrBeginMember e; + assert(std::ranges::begin(e) == &e.x); + assert(std::ranges::cbegin(e) == &e.x); + + return true; +} + + +struct BeginFunction { + int x; + friend constexpr const int *begin(BeginFunction 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); + +struct BeginFunctionReturnsInt { + friend int begin(BeginFunctionReturnsInt const&); +}; +static_assert(!std::is_invocable_v); + +struct BeginFunctionReturnsVoidPtr { + friend void *begin(BeginFunctionReturnsVoidPtr const&); +}; +static_assert(!std::is_invocable_v); + +struct BeginFunctionReturnsPtrConvertible { + struct iterator { operator int*() const; }; + friend iterator begin(BeginFunctionReturnsPtrConvertible const&); +}; +static_assert(!std::is_invocable_v); + +struct BeginFunctionByValue { + friend constexpr int *begin(BeginFunctionByValue) { return &globalBuff[1]; } +}; +static_assert(!std::is_invocable_v); + +struct BeginFunctionEnabledBorrowing { + friend constexpr int *begin(BeginFunctionEnabledBorrowing) { return &globalBuff[2]; } +}; +template<> +inline constexpr bool std::ranges::enable_borrowed_range = true; + +struct BeginFunctionReturnsEmptyPtr { + struct Empty {}; + Empty x; + friend constexpr const Empty *begin(BeginFunctionReturnsEmptyPtr const& bf) { return &bf.x; } +}; + +struct BeginFunctionWithDataMember { + int x; + int begin; + friend constexpr const int *begin(BeginFunctionWithDataMember const& bf) { return &bf.x; } +}; + +struct BeginFunctionWithPrivateBeginMember { + int y; + friend constexpr const int *begin(BeginFunctionWithPrivateBeginMember const& bf) { return &bf.y; } +private: + const int *begin() const; +}; + +constexpr bool testBeginFunction() { + BeginFunction a{}; + const BeginFunction aa{}; + static_assert(!std::invocable); + assert(std::ranges::cbegin(a) == &a.x); + assert(std::ranges::begin(aa) == &aa.x); + assert(std::ranges::cbegin(aa) == &aa.x); + + BeginFunctionByValue b{}; + const BeginFunctionByValue bb{}; + assert(std::ranges::begin(b) == &globalBuff[1]); + assert(std::ranges::cbegin(b) == &globalBuff[1]); + assert(std::ranges::begin(bb) == &globalBuff[1]); + assert(std::ranges::cbegin(bb) == &globalBuff[1]); + + BeginFunctionEnabledBorrowing c{}; + const BeginFunctionEnabledBorrowing cc{}; + assert(std::ranges::begin(std::move(c)) == &globalBuff[2]); + assert(std::ranges::cbegin(std::move(c)) == &globalBuff[2]); + assert(std::ranges::begin(std::move(cc)) == &globalBuff[2]); + assert(std::ranges::cbegin(std::move(cc)) == &globalBuff[2]); + + BeginFunctionReturnsEmptyPtr d{}; + const BeginFunctionReturnsEmptyPtr dd{}; + static_assert(!std::invocable); + assert(std::ranges::cbegin(d) == &d.x); + assert(std::ranges::begin(dd) == &dd.x); + assert(std::ranges::cbegin(dd) == &dd.x); + + BeginFunctionWithDataMember e{}; + const BeginFunctionWithDataMember ee{}; + static_assert(!std::invocable); + assert(std::ranges::begin(ee) == &ee.x); + assert(std::ranges::cbegin(e) == &e.x); + assert(std::ranges::cbegin(ee) == &ee.x); + + BeginFunctionWithPrivateBeginMember f{}; + const BeginFunctionWithPrivateBeginMember ff{}; + static_assert(!std::invocable); + assert(std::ranges::cbegin(f) == &f.y); + assert(std::ranges::begin(ff) == &ff.y); + assert(std::ranges::cbegin(ff) == &ff.y); + + return true; +} + + +ASSERT_NOEXCEPT(std::ranges::begin(std::declval())); +ASSERT_NOEXCEPT(std::ranges::cbegin(std::declval())); + +struct NoThrowMemberBegin { + ThrowingIterator begin() const noexcept; // auto(t.begin()) doesn't throw +} ntmb; +static_assert(noexcept(std::ranges::begin(ntmb))); +static_assert(noexcept(std::ranges::cbegin(ntmb))); + +struct NoThrowADLBegin { + friend ThrowingIterator begin(NoThrowADLBegin&) noexcept; // auto(begin(t)) doesn't throw + friend ThrowingIterator begin(const NoThrowADLBegin&) noexcept; +} ntab; +static_assert(noexcept(std::ranges::begin(ntab))); +static_assert(noexcept(std::ranges::cbegin(ntab))); + +struct NoThrowMemberBeginReturnsRef { + ThrowingIterator& begin() const noexcept; // auto(t.begin()) may throw +} ntmbrr; +static_assert(!noexcept(std::ranges::begin(ntmbrr))); +static_assert(!noexcept(std::ranges::cbegin(ntmbrr))); + +struct BeginReturnsArrayRef { + auto begin() const noexcept -> int(&)[10]; +} brar; +static_assert(noexcept(std::ranges::begin(brar))); +static_assert(noexcept(std::ranges::cbegin(brar))); + +// 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()); + + testBeginMember(); + static_assert(testBeginMember()); + + testBeginFunction(); + static_assert(testBeginFunction()); + + return 0; +} diff --git a/libcudacxx/libcxx/test/std/ranges/range.access/begin.sizezero.pass.cpp b/libcudacxx/libcxx/test/std/ranges/range.access/begin.sizezero.pass.cpp new file mode 100644 index 00000000000..6e71d88ac53 --- /dev/null +++ b/libcudacxx/libcxx/test/std/ranges/range.access/begin.sizezero.pass.cpp @@ -0,0 +1,35 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// UNSUPPORTED: msvc + +// std::ranges::begin +// std::ranges::cbegin +// Test the fix for https://llvm.org/PR54100 + +#include +#include + +#include "test_macros.h" + +struct A { + int m[0]; +}; +static_assert(sizeof(A) == 0); // an extension supported by GCC and Clang + +int main(int, char**) +{ + A a[10]; + std::same_as auto p = std::ranges::begin(a); + assert(p == a); + std::same_as auto cp = std::ranges::cbegin(a); + assert(cp == a); + + return 0; +} diff --git a/libcudacxx/libcxx/test/std/ranges/range.access/begin.verify.cpp b/libcudacxx/libcxx/test/std/ranges/range.access/begin.verify.cpp new file mode 100644 index 00000000000..433404b4ad2 --- /dev/null +++ b/libcudacxx/libcxx/test/std/ranges/range.access/begin.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::begin + +#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::begin` is ill-formed. +void test() { + std::ranges::begin(NonBorrowedRange()); + // expected-error-re@-1 {{{{call to deleted function call operator in type 'const (std::ranges::)?__begin::__fn'}}}} + // expected-error@-2 {{attempt to use a deleted function}} +} diff --git a/libcudacxx/libcxx/test/std/ranges/range.access/end.pass.cpp b/libcudacxx/libcxx/test/std/ranges/range.access/end.pass.cpp new file mode 100644 index 00000000000..21d97354ef0 --- /dev/null +++ b/libcudacxx/libcxx/test/std/ranges/range.access/end.pass.cpp @@ -0,0 +1,370 @@ +//===----------------------------------------------------------------------===// +// +// 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::end +// std::ranges::cend + +#include + +#include +#include +#include "test_macros.h" +#include "test_iterators.h" + +using RangeEndT = decltype(std::ranges::end); +using RangeCEndT = decltype(std::ranges::cend); + +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 EndMember { + int x; + const int *begin() const; + constexpr const int *end() 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::end(x)), int**); + ASSERT_SAME_TYPE(decltype(std::ranges::cend(x)), int* const*); + } + { + int x[2][2]; + ASSERT_SAME_TYPE(decltype(std::ranges::end(x)), int(*)[2]); + ASSERT_SAME_TYPE(decltype(std::ranges::cend(x)), const int(*)[2]); + } + { + struct Different { + char *begin(); + sentinel_wrapper& end(); + short *begin() const; + sentinel_wrapper& end() const; + } x; + ASSERT_SAME_TYPE(decltype(std::ranges::end(x)), sentinel_wrapper); + ASSERT_SAME_TYPE(decltype(std::ranges::cend(x)), sentinel_wrapper); + } + return true; +} + +constexpr bool testArray() { + int a[2]; + assert(std::ranges::end(a) == a + 2); + assert(std::ranges::cend(a) == a + 2); + + int b[2][2]; + assert(std::ranges::end(b) == b + 2); + assert(std::ranges::cend(b) == b + 2); + + EndMember c[2]; + assert(std::ranges::end(c) == c + 2); + assert(std::ranges::cend(c) == c + 2); + + return true; +} + +struct EndMemberReturnsInt { + int begin() const; + int end() const; +}; +static_assert(!std::is_invocable_v); + +struct EndMemberReturnsVoidPtr { + const void *begin() const; + const void *end() const; +}; +static_assert(!std::is_invocable_v); + +struct PtrConvertible { + operator int*() const; +}; +struct PtrConvertibleEndMember { + PtrConvertible begin() const; + PtrConvertible end() const; +}; +static_assert(!std::is_invocable_v); + +struct NoBeginMember { + constexpr const int *end(); +}; +static_assert(!std::is_invocable_v); + +struct NonConstEndMember { + int x; + constexpr int *begin() { return nullptr; } + constexpr int *end() { 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 EnabledBorrowingEndMember { + constexpr int *begin() const { return nullptr; } + constexpr int *end() const { return &globalBuff[0]; } +}; + +template<> +inline constexpr bool std::ranges::enable_borrowed_range = true; + +struct EndMemberFunction { + int x; + constexpr const int *begin() const { return nullptr; } + constexpr const int *end() const { return &x; } + friend constexpr int *end(EndMemberFunction const&); +}; + +struct Empty { }; +struct EmptyEndMember { + Empty begin() const; + Empty end() const; +}; +static_assert(!std::is_invocable_v); + +struct EmptyPtrEndMember { + Empty x; + constexpr const Empty *begin() const { return nullptr; } + constexpr const Empty *end() const { return &x; } +}; + +constexpr bool testEndMember() { + EndMember a; + assert(std::ranges::end(a) == &a.x); + assert(std::ranges::cend(a) == &a.x); + + NonConstEndMember b; + assert(std::ranges::end(b) == &b.x); + static_assert(!std::is_invocable_v); + + EnabledBorrowingEndMember c; + assert(std::ranges::end(std::move(c)) == &globalBuff[0]); + assert(std::ranges::cend(std::move(c)) == &globalBuff[0]); + + EndMemberFunction d; + assert(std::ranges::end(d) == &d.x); + assert(std::ranges::cend(d) == &d.x); + + EmptyPtrEndMember e; + assert(std::ranges::end(e) == &e.x); + assert(std::ranges::cend(e) == &e.x); + + return true; +} + +struct EndFunction { + int x; + friend constexpr const int *begin(EndFunction const&) { return nullptr; } + friend constexpr const int *end(EndFunction 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 EndFunctionReturnsInt { + friend constexpr int begin(EndFunctionReturnsInt const&); + friend constexpr int end(EndFunctionReturnsInt const&); +}; +static_assert(!std::is_invocable_v); + +struct EndFunctionReturnsVoidPtr { + friend constexpr void *begin(EndFunctionReturnsVoidPtr const&); + friend constexpr void *end(EndFunctionReturnsVoidPtr const&); +}; +static_assert(!std::is_invocable_v); + +struct EndFunctionReturnsEmpty { + friend constexpr Empty begin(EndFunctionReturnsEmpty const&); + friend constexpr Empty end(EndFunctionReturnsEmpty const&); +}; +static_assert(!std::is_invocable_v); + +struct EndFunctionReturnsPtrConvertible { + friend constexpr PtrConvertible begin(EndFunctionReturnsPtrConvertible const&); + friend constexpr PtrConvertible end(EndFunctionReturnsPtrConvertible const&); +}; +static_assert(!std::is_invocable_v); + +struct NoBeginFunction { + friend constexpr const int *end(NoBeginFunction const&); +}; +static_assert(!std::is_invocable_v); + +struct EndFunctionByValue { + friend constexpr int *begin(EndFunctionByValue) { return nullptr; } + friend constexpr int *end(EndFunctionByValue) { return &globalBuff[1]; } +}; +static_assert(!std::is_invocable_v); + +struct EndFunctionEnabledBorrowing { + friend constexpr int *begin(EndFunctionEnabledBorrowing) { return nullptr; } + friend constexpr int *end(EndFunctionEnabledBorrowing) { return &globalBuff[2]; } +}; +template<> +inline constexpr bool std::ranges::enable_borrowed_range = true; + +struct EndFunctionReturnsEmptyPtr { + Empty x; + friend constexpr const Empty *begin(EndFunctionReturnsEmptyPtr const&) { return nullptr; } + friend constexpr const Empty *end(EndFunctionReturnsEmptyPtr const& bf) { return &bf.x; } +}; + +struct EndFunctionWithDataMember { + int x; + int end; + friend constexpr const int *begin(EndFunctionWithDataMember const&) { return nullptr; } + friend constexpr const int *end(EndFunctionWithDataMember const& bf) { return &bf.x; } +}; + +struct EndFunctionWithPrivateEndMember { + int y; + friend constexpr const int *begin(EndFunctionWithPrivateEndMember const&) { return nullptr; } + friend constexpr const int *end(EndFunctionWithPrivateEndMember const& bf) { return &bf.y; } +private: + const int *end() const; +}; + +struct BeginMemberEndFunction { + int x; + constexpr const int *begin() const { return nullptr; } + friend constexpr const int *end(BeginMemberEndFunction const& bf) { return &bf.x; } +}; + +constexpr bool testEndFunction() { + const EndFunction a{}; + assert(std::ranges::end(a) == &a.x); + assert(std::ranges::cend(a) == &a.x); + EndFunction aa{}; + static_assert(!std::is_invocable_v); + assert(std::ranges::cend(aa) == &aa.x); + + EndFunctionByValue b; + assert(std::ranges::end(b) == &globalBuff[1]); + assert(std::ranges::cend(b) == &globalBuff[1]); + + EndFunctionEnabledBorrowing c; + assert(std::ranges::end(std::move(c)) == &globalBuff[2]); + assert(std::ranges::cend(std::move(c)) == &globalBuff[2]); + + const EndFunctionReturnsEmptyPtr d{}; + assert(std::ranges::end(d) == &d.x); + assert(std::ranges::cend(d) == &d.x); + EndFunctionReturnsEmptyPtr dd{}; + static_assert(!std::is_invocable_v); + assert(std::ranges::cend(dd) == &dd.x); + + const EndFunctionWithDataMember e{}; + assert(std::ranges::end(e) == &e.x); + assert(std::ranges::cend(e) == &e.x); + EndFunctionWithDataMember ee{}; + static_assert(!std::is_invocable_v); + assert(std::ranges::cend(ee) == &ee.x); + + const EndFunctionWithPrivateEndMember f{}; + assert(std::ranges::end(f) == &f.y); + assert(std::ranges::cend(f) == &f.y); + EndFunctionWithPrivateEndMember ff{}; + static_assert(!std::is_invocable_v); + assert(std::ranges::cend(ff) == &ff.y); + + const BeginMemberEndFunction g{}; + assert(std::ranges::end(g) == &g.x); + assert(std::ranges::cend(g) == &g.x); + BeginMemberEndFunction gg{}; + static_assert(!std::is_invocable_v); + assert(std::ranges::cend(gg) == &gg.x); + + return true; +} + + +ASSERT_NOEXCEPT(std::ranges::end(std::declval())); +ASSERT_NOEXCEPT(std::ranges::cend(std::declval())); + +struct NoThrowMemberEnd { + ThrowingIterator begin() const; + ThrowingIterator end() const noexcept; // auto(t.end()) doesn't throw +} ntme; +static_assert(noexcept(std::ranges::end(ntme))); +static_assert(noexcept(std::ranges::cend(ntme))); + +struct NoThrowADLEnd { + ThrowingIterator begin() const; + friend ThrowingIterator end(NoThrowADLEnd&) noexcept; // auto(end(t)) doesn't throw + friend ThrowingIterator end(const NoThrowADLEnd&) noexcept; +} ntae; +static_assert(noexcept(std::ranges::end(ntae))); +static_assert(noexcept(std::ranges::cend(ntae))); + +struct NoThrowMemberEndReturnsRef { + ThrowingIterator begin() const; + ThrowingIterator& end() const noexcept; // auto(t.end()) may throw +} ntmerr; +static_assert(!noexcept(std::ranges::end(ntmerr))); +static_assert(!noexcept(std::ranges::cend(ntmerr))); + +struct EndReturnsArrayRef { + auto begin() const noexcept -> int(&)[10]; + auto end() const noexcept -> int(&)[10]; +} erar; +static_assert(noexcept(std::ranges::end(erar))); +static_assert(noexcept(std::ranges::cend(erar))); + +// 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()); + + testEndMember(); + static_assert(testEndMember()); + + testEndFunction(); + static_assert(testEndFunction()); + + return 0; +} diff --git a/libcudacxx/libcxx/test/std/ranges/range.access/end.sizezero.pass.cpp b/libcudacxx/libcxx/test/std/ranges/range.access/end.sizezero.pass.cpp new file mode 100644 index 00000000000..304ef2d97ac --- /dev/null +++ b/libcudacxx/libcxx/test/std/ranges/range.access/end.sizezero.pass.cpp @@ -0,0 +1,35 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// UNSUPPORTED: msvc + +// std::ranges::end +// std::ranges::cend +// Test the fix for https://llvm.org/PR54100 + +#include +#include + +#include "test_macros.h" + +struct A { + int m[0]; +}; +static_assert(sizeof(A) == 0); // an extension supported by GCC and Clang + +int main(int, char**) +{ + A a[10]; + std::same_as auto p = std::ranges::end(a); + assert(p == a + 10); + std::same_as auto cp = std::ranges::cend(a); + assert(cp == a + 10); + + return 0; +} diff --git a/libcudacxx/libcxx/test/std/ranges/range.access/end.verify.cpp b/libcudacxx/libcxx/test/std/ranges/range.access/end.verify.cpp new file mode 100644 index 00000000000..a4a7f78b8c6 --- /dev/null +++ b/libcudacxx/libcxx/test/std/ranges/range.access/end.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::end + +#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::end` is ill-formed. +void test() { + std::ranges::end(NonBorrowedRange()); + // expected-error-re@-1 {{{{call to deleted function call operator in type 'const (std::ranges::)?__end::__fn'}}}} + // expected-error@-2 {{attempt to use a deleted function}} +} diff --git a/libcudacxx/test/libcudacxx/std/ranges/range.access/begin.pass.cpp b/libcudacxx/test/libcudacxx/std/ranges/range.access/begin.pass.cpp new file mode 100644 index 00000000000..89e7209a88a --- /dev/null +++ b/libcudacxx/test/libcudacxx/std/ranges/range.access/begin.pass.cpp @@ -0,0 +1,370 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// std::ranges::begin +// std::ranges::cbegin + +#include + +#include +#include + +#include "test_macros.h" +#include "test_iterators.h" + +using RangeBeginT = decltype(cuda::std::ranges::begin); +using RangeCBeginT = decltype(cuda::std::ranges::cbegin); + +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); + +// This has been made valid as a defect report for C++17 onwards, however both clang and gcc below 11.0 does not implement it +#if (!defined(__GNUC__) || __GNUC__ >= 11) +static_assert( cuda::std::is_invocable_v); +#endif +static_assert(!cuda::std::is_invocable_v); +static_assert( cuda::std::is_invocable_v); +static_assert(!cuda::std::is_invocable_v); +// This has been made valid as a defect report for C++17 onwards, however both clang and gcc below 11.0 does not implement it +#if (!defined(__GNUC__) || __GNUC__ >= 11) +static_assert( cuda::std::is_invocable_v); +#endif + +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); + +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); + +// This case is IFNDR; we handle it SFINAE-friendly. +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); + +// This case is IFNDR; we handle it SFINAE-friendly. +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 BeginMember { + int x; + __host__ __device__ constexpr const int *begin() 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__ short*& begin() 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::begin(a) == a); + assert(cuda::std::ranges::cbegin(a) == a); + + int b[2][2] = {}; + assert(cuda::std::ranges::begin(b) == b); + assert(cuda::std::ranges::cbegin(b) == b); + + BeginMember c[2] = {}; + assert(cuda::std::ranges::begin(c) == c); + assert(cuda::std::ranges::cbegin(c) == c); + + return true; +} + +struct BeginMemberReturnsInt { + __host__ __device__ int begin() const; +}; +static_assert(!cuda::std::is_invocable_v); + +struct BeginMemberReturnsVoidPtr { + __host__ __device__ const void *begin() const; +}; +static_assert(!cuda::std::is_invocable_v); + +struct EmptyBeginMember { + struct iterator {}; + __host__ __device__ iterator begin() const; +}; +static_assert(!cuda::std::is_invocable_v); + +struct PtrConvertibleBeginMember { + struct iterator { __host__ __device__ operator int*() const; }; + __host__ __device__ iterator begin() const; +}; +static_assert(!cuda::std::is_invocable_v); + +struct NonConstBeginMember { + int x; + __host__ __device__ constexpr int *begin() { 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 EnabledBorrowingBeginMember { + __host__ __device__ constexpr const int *begin() const { return &globalBuff[0]; } +}; +template<> +inline constexpr bool cuda::std::ranges::enable_borrowed_range = true; + +struct BeginMemberFunction { + int x; + __host__ __device__ constexpr const int *begin() const { return &x; } + __host__ __device__ friend int *begin(BeginMemberFunction const&); +}; + +struct EmptyPtrBeginMember { + struct Empty {}; + Empty x; + __host__ __device__ constexpr const Empty *begin() const { return &x; } +}; + +__host__ __device__ constexpr bool testBeginMember() { + BeginMember a{}; + assert(cuda::std::ranges::begin(a) == &a.x); + assert(cuda::std::ranges::cbegin(a) == &a.x); + static_assert(!cuda::std::is_invocable_v); + static_assert(!cuda::std::is_invocable_v); + + NonConstBeginMember b{}; + assert(cuda::std::ranges::begin(b) == &b.x); + static_assert(!cuda::std::is_invocable_v); + + EnabledBorrowingBeginMember c{}; + assert(cuda::std::ranges::begin(c) == &globalBuff[0]); + assert(cuda::std::ranges::cbegin(c) == &globalBuff[0]); + assert(cuda::std::ranges::begin(cuda::std::move(c)) == &globalBuff[0]); + assert(cuda::std::ranges::cbegin(cuda::std::move(c)) == &globalBuff[0]); + + BeginMemberFunction d{}; + assert(cuda::std::ranges::begin(d) == &d.x); + assert(cuda::std::ranges::cbegin(d) == &d.x); + + EmptyPtrBeginMember e{}; + assert(cuda::std::ranges::begin(e) == &e.x); + assert(cuda::std::ranges::cbegin(e) == &e.x); + + return true; +} + + +struct BeginFunction { + int x; + __host__ __device__ friend constexpr const int *begin(BeginFunction 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); + +struct BeginFunctionReturnsInt { + __host__ __device__ friend int begin(BeginFunctionReturnsInt const&); +}; +static_assert(!cuda::std::is_invocable_v); + +struct BeginFunctionReturnsVoidPtr { + __host__ __device__ friend void *begin(BeginFunctionReturnsVoidPtr const&); +}; +static_assert(!cuda::std::is_invocable_v); + +struct BeginFunctionReturnsPtrConvertible { + struct iterator { __host__ __device__ operator int*() const; }; + __host__ __device__ friend iterator begin(BeginFunctionReturnsPtrConvertible const&); +}; +static_assert(!cuda::std::is_invocable_v); + +struct BeginFunctionByValue { + __host__ __device__ friend constexpr const int *begin(BeginFunctionByValue) { return &globalBuff[1]; } +}; +static_assert(!cuda::std::is_invocable_v); + +struct BeginFunctionEnabledBorrowing { + __host__ __device__ friend constexpr const int *begin(BeginFunctionEnabledBorrowing) { return &globalBuff[2]; } +}; +template<> +inline constexpr bool cuda::std::ranges::enable_borrowed_range = true; + +struct BeginFunctionReturnsEmptyPtr { + struct Empty {}; + Empty x; + __host__ __device__ friend constexpr const Empty *begin(BeginFunctionReturnsEmptyPtr const& bf) { return &bf.x; } +}; + +struct BeginFunctionWithDataMember { + int x; + int begin; + __host__ __device__ friend constexpr const int *begin(BeginFunctionWithDataMember const& bf) { return &bf.x; } +}; + +struct BeginFunctionWithPrivateBeginMember { + int y; + __host__ __device__ friend constexpr const int *begin(BeginFunctionWithPrivateBeginMember const& bf) { return &bf.y; } +private: + __host__ __device__ const int *begin() const; +}; + +__host__ __device__ constexpr bool testBeginFunction() { + BeginFunction a{}; + const BeginFunction aa{}; + static_assert(!cuda::std::invocable); + assert(cuda::std::ranges::cbegin(a) == &a.x); + assert(cuda::std::ranges::begin(aa) == &aa.x); + assert(cuda::std::ranges::cbegin(aa) == &aa.x); + + BeginFunctionByValue b{}; + const BeginFunctionByValue bb{}; + assert(cuda::std::ranges::begin(b) == &globalBuff[1]); + assert(cuda::std::ranges::cbegin(b) == &globalBuff[1]); + assert(cuda::std::ranges::begin(bb) == &globalBuff[1]); + assert(cuda::std::ranges::cbegin(bb) == &globalBuff[1]); + + BeginFunctionEnabledBorrowing c{}; + const BeginFunctionEnabledBorrowing cc{}; + assert(cuda::std::ranges::begin(cuda::std::move(c)) == &globalBuff[2]); + assert(cuda::std::ranges::cbegin(cuda::std::move(c)) == &globalBuff[2]); + assert(cuda::std::ranges::begin(cuda::std::move(cc)) == &globalBuff[2]); + assert(cuda::std::ranges::cbegin(cuda::std::move(cc)) == &globalBuff[2]); + + BeginFunctionReturnsEmptyPtr d{}; + const BeginFunctionReturnsEmptyPtr dd{}; + static_assert(!cuda::std::invocable); + assert(cuda::std::ranges::cbegin(d) == &d.x); + assert(cuda::std::ranges::begin(dd) == &dd.x); + assert(cuda::std::ranges::cbegin(dd) == &dd.x); + + BeginFunctionWithDataMember e{}; + const BeginFunctionWithDataMember ee{}; + static_assert(!cuda::std::invocable); + assert(cuda::std::ranges::begin(ee) == &ee.x); + assert(cuda::std::ranges::cbegin(e) == &e.x); + assert(cuda::std::ranges::cbegin(ee) == &ee.x); + + BeginFunctionWithPrivateBeginMember f{}; + const BeginFunctionWithPrivateBeginMember ff{}; + static_assert(!cuda::std::invocable); + assert(cuda::std::ranges::cbegin(f) == &f.y); + assert(cuda::std::ranges::begin(ff) == &ff.y); + assert(cuda::std::ranges::cbegin(ff) == &ff.y); + + return true; +} +ASSERT_NOEXCEPT(cuda::std::ranges::begin(cuda::std::declval())); +ASSERT_NOEXCEPT(cuda::std::ranges::cbegin(cuda::std::declval())); + +#if !defined(TEST_COMPILER_MSVC_2019) // broken noexcept +_LIBCUDACXX_CPO_ACCESSIBILITY struct NoThrowMemberBegin { + __host__ __device__ ThrowingIterator begin() const noexcept; // auto(t.begin()) doesn't throw +} ntmb; +static_assert(noexcept(cuda::std::ranges::begin(ntmb))); +static_assert(noexcept(cuda::std::ranges::cbegin(ntmb))); + +_LIBCUDACXX_CPO_ACCESSIBILITY struct NoThrowADLBegin { + __host__ __device__ friend ThrowingIterator begin(NoThrowADLBegin&) noexcept; // auto(begin(t)) doesn't throw + __host__ __device__ friend ThrowingIterator begin(const NoThrowADLBegin&) noexcept; +} ntab; +static_assert(noexcept(cuda::std::ranges::begin(ntab))); +static_assert(noexcept(cuda::std::ranges::cbegin(ntab))); +#endif // !TEST_COMPILER_MSVC_2019 + +#if !defined(TEST_COMPILER_ICC) +_LIBCUDACXX_CPO_ACCESSIBILITY struct NoThrowMemberBeginReturnsRef { + __host__ __device__ ThrowingIterator& begin() const noexcept; // auto(t.begin()) may throw +} ntmbrr; +static_assert(!noexcept(cuda::std::ranges::begin(ntmbrr))); +static_assert(!noexcept(cuda::std::ranges::cbegin(ntmbrr))); +#endif // !TEST_COMPILER_ICC + +_LIBCUDACXX_CPO_ACCESSIBILITY struct BeginReturnsArrayRef { + __host__ __device__ auto begin() const noexcept -> int(&)[10]; +} brar; +static_assert(noexcept(cuda::std::ranges::begin(brar))); +static_assert(noexcept(cuda::std::ranges::cbegin(brar))); + +#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 + + testBeginMember(); + static_assert(testBeginMember()); + + testBeginFunction(); + static_assert(testBeginFunction()); + +#if !defined(TEST_COMPILER_MSVC_2019) // broken noexcept + unused(ntmb); + unused(ntab); +#endif // !TEST_COMPILER_MSVC_2019 +#if !defined(TEST_COMPILER_ICC) + unused(ntmbrr); +#endif // !TEST_COMPILER_ICC + unused(brar); + + return 0; +} diff --git a/libcudacxx/test/libcudacxx/std/ranges/range.access/begin.sizezero.pass.cpp b/libcudacxx/test/libcudacxx/std/ranges/range.access/begin.sizezero.pass.cpp new file mode 100644 index 00000000000..03468521542 --- /dev/null +++ b/libcudacxx/test/libcudacxx/std/ranges/range.access/begin.sizezero.pass.cpp @@ -0,0 +1,43 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// cuda::std::ranges::begin +// cuda::std::ranges::cbegin +// Test the fix for https://llvm.org/PR54100 + +#include +#include + +#include "test_macros.h" + +#ifndef __CUDA_ARCH__ +struct A { + int m[0]; +}; +static_assert(sizeof(A) == 0); // an extension supported by GCC and Clang + +__device__ static A a[10]; + +int main(int, char**) +{ + auto p = cuda::std::ranges::begin(a); + static_assert(cuda::std::same_as); + assert(p == a); + auto cp = cuda::std::ranges::cbegin(a); + static_assert(cuda::std::same_as); + assert(cp == a); + + return 0; +} +#else +int main(int, char**) { return 0; } +#endif diff --git a/libcudacxx/test/libcudacxx/std/ranges/range.access/begin.verify.cpp b/libcudacxx/test/libcudacxx/std/ranges/range.access/begin.verify.cpp new file mode 100644 index 00000000000..11ceadc7560 --- /dev/null +++ b/libcudacxx/test/libcudacxx/std/ranges/range.access/begin.verify.cpp @@ -0,0 +1,33 @@ +//===----------------------------------------------------------------------===// +// +// 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::begin + +#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::begin` is ill-formed. +__host__ __device__ void test() { + cuda::std::ranges::begin(NonBorrowedRange()); + // expected-error-re@-1 {{{{call to deleted function call operator in type 'const (cuda::std::ranges::)?__begin::__fn'}}}} + // expected-error@-2 {{attempt to use a deleted function}} +} + +int main(int, char**) +{ + return 0; +} diff --git a/libcudacxx/test/libcudacxx/std/ranges/range.access/end.pass.cpp b/libcudacxx/test/libcudacxx/std/ranges/range.access/end.pass.cpp new file mode 100644 index 00000000000..37d3ac4f059 --- /dev/null +++ b/libcudacxx/test/libcudacxx/std/ranges/range.access/end.pass.cpp @@ -0,0 +1,391 @@ +//===----------------------------------------------------------------------===// +// +// 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::end +// cuda::std::ranges::cend + +#include + +#include +#include + +#include "test_macros.h" +#include "test_iterators.h" + +using RangeEndT = decltype(cuda::std::ranges::end); +using RangeCEndT = decltype(cuda::std::ranges::cend); + +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 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]; } +}; +template<> +inline constexpr bool cuda::std::ranges::enable_borrowed_range = true; + +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]; } +}; +template<> +inline constexpr bool cuda::std::ranges::enable_borrowed_range = true; + +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; +} +ASSERT_NOEXCEPT(cuda::std::ranges::end(cuda::std::declval())); +ASSERT_NOEXCEPT(cuda::std::ranges::cend(cuda::std::declval())); + +#if !defined(TEST_COMPILER_MSVC_2019) // broken noexcept +_LIBCUDACXX_CPO_ACCESSIBILITY 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))); + +_LIBCUDACXX_CPO_ACCESSIBILITY 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_MSVC_2019 + +#if !defined(TEST_COMPILER_ICC) +_LIBCUDACXX_CPO_ACCESSIBILITY 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))); +#endif // !TEST_COMPILER_ICC + +_LIBCUDACXX_CPO_ACCESSIBILITY 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(); +#ifndef TEST_COMPILER_CUDACC_BELOW_11_3 + static_assert(testArray()); +#endif // TEST_COMPILER_CUDACC_BELOW_11_3 + + testEndMember(); + static_assert(testEndMember()); + + testEndFunction(); + static_assert(testEndFunction()); + +#if !defined(TEST_COMPILER_MSVC_2019) // broken noexcept + unused(ntme); + unused(ntae); +#endif // !TEST_COMPILER_MSVC_2019 +#if !defined(TEST_COMPILER_ICC) + unused(ntmerr); +#endif // !TEST_COMPILER_ICC + unused(erar); + + return 0; +} diff --git a/libcudacxx/test/libcudacxx/std/ranges/range.access/end.sizezero.pass.cpp b/libcudacxx/test/libcudacxx/std/ranges/range.access/end.sizezero.pass.cpp new file mode 100644 index 00000000000..0ce7b8683f9 --- /dev/null +++ b/libcudacxx/test/libcudacxx/std/ranges/range.access/end.sizezero.pass.cpp @@ -0,0 +1,43 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// cuda::std::ranges::end +// cuda::std::ranges::cend +// Test the fix for https://llvm.org/PR54100 + +#include +#include + +#include "test_macros.h" + +#ifndef __CUDA_ARCH__ +struct A { + int m[0]; +}; +static_assert(sizeof(A) == 0); // an extension supported by GCC and Clang + +__device__ static A a[10]; + +int main(int, char**) +{ + auto p = cuda::std::ranges::end(a); + static_assert(cuda::std::same_as); + assert(p == a + 10); + auto cp = cuda::std::ranges::cend(a); + static_assert(cuda::std::same_as); + assert(cp == a + 10); + + return 0; +} +#else +int main(int, char**) { return 0; } +#endif diff --git a/libcudacxx/test/libcudacxx/std/ranges/range.access/end.verify.cpp b/libcudacxx/test/libcudacxx/std/ranges/range.access/end.verify.cpp new file mode 100644 index 00000000000..5270f782324 --- /dev/null +++ b/libcudacxx/test/libcudacxx/std/ranges/range.access/end.verify.cpp @@ -0,0 +1,33 @@ +//===----------------------------------------------------------------------===// +// +// 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::end + +#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::end` is ill-formed. +__host__ __device__ void test() { + cuda::std::ranges::end(NonBorrowedRange()); + // expected-error-re@-1 {{{{call to deleted function call operator in type 'const (cuda::std::ranges::)?__end::__fn'}}}} + // expected-error@-2 {{attempt to use a deleted function}} +} + +int main(int, char**) +{ + return 0; +}