From 6bbaac99fec7c2c22a34742a842edad87b62c0b5 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Thu, 25 Jan 2024 11:15:24 +0100 Subject: [PATCH] Implement `ranges::size` and `ranges::ssize` Fixes #1329 --- .../std/detail/libcxx/include/CMakeLists.txt | 1 + .../std/detail/libcxx/include/__ranges/size.h | 219 +++++++++++ .../cuda/std/detail/libcxx/include/ranges | 1 + .../std/ranges/range.access/size.pass.cpp | 336 ++++++++++++++++ .../std/ranges/range.access/size.verify.cpp | 21 + .../std/ranges/range.access/ssize.pass.cpp | 90 +++++ .../std/ranges/range.access/ssize.verify.cpp | 21 + .../std/ranges/range.access/size.pass.cpp | 365 ++++++++++++++++++ .../std/ranges/range.access/size.verify.cpp | 24 ++ .../std/ranges/range.access/ssize.pass.cpp | 94 +++++ .../std/ranges/range.access/ssize.verify.cpp | 24 ++ 11 files changed, 1196 insertions(+) create mode 100644 libcudacxx/include/cuda/std/detail/libcxx/include/__ranges/size.h create mode 100644 libcudacxx/libcxx/test/std/ranges/range.access/size.pass.cpp create mode 100644 libcudacxx/libcxx/test/std/ranges/range.access/size.verify.cpp create mode 100644 libcudacxx/libcxx/test/std/ranges/range.access/ssize.pass.cpp create mode 100644 libcudacxx/libcxx/test/std/ranges/range.access/ssize.verify.cpp create mode 100644 libcudacxx/test/libcudacxx/std/ranges/range.access/size.pass.cpp create mode 100644 libcudacxx/test/libcudacxx/std/ranges/range.access/size.verify.cpp create mode 100644 libcudacxx/test/libcudacxx/std/ranges/range.access/ssize.pass.cpp create mode 100644 libcudacxx/test/libcudacxx/std/ranges/range.access/ssize.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 5b7220bf38e..1cdc91ca81e 100644 --- a/libcudacxx/include/cuda/std/detail/libcxx/include/CMakeLists.txt +++ b/libcudacxx/include/cuda/std/detail/libcxx/include/CMakeLists.txt @@ -192,6 +192,7 @@ set(files __ranges/enable_view.h __ranges/rbegin.h __ranges/rend.h + __ranges/size.h __split_buffer __sso_allocator __std_stream diff --git a/libcudacxx/include/cuda/std/detail/libcxx/include/__ranges/size.h b/libcudacxx/include/cuda/std/detail/libcxx/include/__ranges/size.h new file mode 100644 index 00000000000..5b8793348bb --- /dev/null +++ b/libcudacxx/include/cuda/std/detail/libcxx/include/__ranges/size.h @@ -0,0 +1,219 @@ +// -*- 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) 2024 NVIDIA CORPORATION & AFFILIATES. +// +//===----------------------------------------------------------------------===// +#ifndef _LIBCUDACXX___RANGES_SIZE_H +#define _LIBCUDACXX___RANGES_SIZE_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/arithmetic.h" +#include "../__concepts/class_or_enum.h" +#include "../__iterator/concepts.h" +#include "../__iterator/iterator_traits.h" +#include "../__memory/pointer_traits.h" +#include "../__ranges/access.h" +#include "../__type_traits/is_unbounded_array.h" +#include "../__type_traits/make_unsigned.h" +#include "../__type_traits/make_signed.h" +#include "../__type_traits/remove_cvref.h" +#include "../__utility/auto_cast.h" +#include "../__utility/declval.h" +#include "../cstddef" +#include "../cstdlib" + +_LIBCUDACXX_BEGIN_NAMESPACE_RANGES + +#if _CCCL_STD_VER >= 2017 && !defined(_LIBCUDACXX_COMPILER_MSVC_2017) + +template +_LIBCUDACXX_INLINE_VAR constexpr bool disable_sized_range = false; + +// [range.prim.size] + +_LIBCUDACXX_BEGIN_NAMESPACE_CPO(__size) +template +void size(_Tp&) = delete; +template +void size(const _Tp&) = delete; + +template +_LIBCUDACXX_CONCEPT __size_enabled = !disable_sized_range>; + +# if _CCCL_STD_VER >= 2020 +template +concept __member_size = __size_enabled<_Tp> && __workaround_52970<_Tp> && requires(_Tp&& __t) { + { + _LIBCUDACXX_AUTO_CAST(__t.size()) + } -> __integer_like; +}; + +template +concept __unqualified_size = + __size_enabled<_Tp> && !__member_size<_Tp> && __class_or_enum> && requires(_Tp&& __t) { + { + _LIBCUDACXX_AUTO_CAST(size(__t)) + } -> __integer_like; + }; + +template +concept __difference = + !__member_size<_Tp> && !__unqualified_size<_Tp> && __class_or_enum> && requires(_Tp&& __t) { + { + _CUDA_VRANGES::begin(__t) + } -> forward_iterator; + { + _CUDA_VRANGES::end(__t) + } -> sized_sentinel_for()))>; + }; +# else // ^^^ CXX20 ^^^ / vvv CXX17 vvv +template +_LIBCUDACXX_CONCEPT_FRAGMENT( + __member_size_, + requires(_Tp&& __t)(requires(__size_enabled<_Tp>), + requires(__workaround_52970<_Tp>), + requires(__integer_like))); + +template +_LIBCUDACXX_CONCEPT __member_size = _LIBCUDACXX_FRAGMENT(__member_size_, _Tp); + +template +_LIBCUDACXX_CONCEPT_FRAGMENT( + __unqualified_size_, + requires(_Tp&& __t)(requires(__size_enabled<_Tp>), + requires(!__member_size<_Tp>), + requires(__class_or_enum>), + requires(__integer_like))); + +template +_LIBCUDACXX_CONCEPT __unqualified_size = _LIBCUDACXX_FRAGMENT(__unqualified_size_, _Tp); + +template +_LIBCUDACXX_CONCEPT_FRAGMENT( + __difference_, + requires(_Tp&& __t)(requires(!__member_size<_Tp>), + requires(!__unqualified_size<_Tp>), + requires(__class_or_enum>), + requires(forward_iterator), + requires(sized_sentinel_for()))>))); + +template +_LIBCUDACXX_CONCEPT __difference = _LIBCUDACXX_FRAGMENT(__difference_, _Tp); +# endif // _CCCL_STD_VER <= 2017 + +struct __fn +{ + // `[range.prim.size]`: the array case (for rvalues). + template + _LIBCUDACXX_NODISCARD_ATTRIBUTE _LIBCUDACXX_HIDE_FROM_ABI _LIBCUDACXX_INLINE_VISIBILITY constexpr size_t + operator()(_Tp (&&)[_Sz]) const noexcept + { + return _Sz; + } + + // `[range.prim.size]`: the array case (for lvalues). + template + _LIBCUDACXX_NODISCARD_ATTRIBUTE _LIBCUDACXX_HIDE_FROM_ABI _LIBCUDACXX_INLINE_VISIBILITY constexpr size_t + operator()(_Tp (&)[_Sz]) const noexcept + { + return _Sz; + } + + // `[range.prim.size]`: `auto(t.size())` is a valid expression. + _LIBCUDACXX_TEMPLATE(class _Tp) + _LIBCUDACXX_REQUIRES(__member_size<_Tp>) + _LIBCUDACXX_NODISCARD_ATTRIBUTE _LIBCUDACXX_HIDE_FROM_ABI _LIBCUDACXX_INLINE_VISIBILITY constexpr auto + operator()(_Tp&& __t) const noexcept(noexcept(_LIBCUDACXX_AUTO_CAST(__t.size()))) + { + return _LIBCUDACXX_AUTO_CAST(__t.size()); + } + + // `[range.prim.size]`: `auto(size(t))` is a valid expression. + _LIBCUDACXX_TEMPLATE(class _Tp) + _LIBCUDACXX_REQUIRES(__unqualified_size<_Tp>) + _LIBCUDACXX_NODISCARD_ATTRIBUTE _LIBCUDACXX_HIDE_FROM_ABI _LIBCUDACXX_INLINE_VISIBILITY constexpr auto + operator()(_Tp&& __t) const noexcept(noexcept(_LIBCUDACXX_AUTO_CAST(size(__t)))) + { + return _LIBCUDACXX_AUTO_CAST(size(__t)); + } + + // [range.prim.size]: the `to-unsigned-like` case. + _LIBCUDACXX_TEMPLATE(class _Tp) + _LIBCUDACXX_REQUIRES(__difference<_Tp>) + _LIBCUDACXX_NODISCARD_ATTRIBUTE _LIBCUDACXX_HIDE_FROM_ABI _LIBCUDACXX_INLINE_VISIBILITY constexpr auto + operator()(_Tp&& __t) const + noexcept(noexcept(_CUDA_VSTD::__to_unsigned_like(_CUDA_VRANGES::end(__t) - _CUDA_VRANGES::begin(__t)))) + -> decltype(_CUDA_VSTD::__to_unsigned_like(_CUDA_VRANGES::end(__t) - _CUDA_VRANGES::begin(__t))) + { + return _CUDA_VSTD::__to_unsigned_like(_CUDA_VRANGES::end(__t) - _CUDA_VRANGES::begin(__t)); + } +}; +_LIBCUDACXX_END_NAMESPACE_CPO + +inline namespace __cpo +{ +_LIBCUDACXX_CPO_ACCESSIBILITY auto size = __size::__fn{}; +} // namespace __cpo + +// [range.prim.ssize] + +_LIBCUDACXX_BEGIN_NAMESPACE_CPO(__ssize) +# if _CCCL_STD_VER >= 2020 +template +concept __can_ssize = requires(_Tp&& __t) { _CUDA_VRANGES::size(__t); }; +# else // ^^^ CXX20 ^^^ / vvv CXX17 vvv +template +_LIBCUDACXX_CONCEPT_FRAGMENT( + __can_ssize_, requires(_Tp&& __t)(requires(!is_unbounded_array_v<_Tp>), ((void) _CUDA_VRANGES::size(__t)))); + +template +_LIBCUDACXX_CONCEPT __can_ssize = _LIBCUDACXX_FRAGMENT(__can_ssize_, _Tp); +# endif // _CCCL_STD_VER <= 2017 + +struct __fn +{ + _LIBCUDACXX_TEMPLATE(class _Tp) + _LIBCUDACXX_REQUIRES(__can_ssize<_Tp>) + _LIBCUDACXX_NODISCARD_ATTRIBUTE _LIBCUDACXX_HIDE_FROM_ABI _LIBCUDACXX_INLINE_VISIBILITY constexpr auto + operator()(_Tp&& __t) const noexcept(noexcept(_CUDA_VRANGES::size(__t))) + { + using _Signed = make_signed_t; + if constexpr (sizeof(ptrdiff_t) > sizeof(_Signed)) + { + return static_cast(_CUDA_VRANGES::size(__t)); + } + else + { + return static_cast<_Signed>(_CUDA_VRANGES::size(__t)); + } + _LIBCUDACXX_UNREACHABLE(); + } +}; +_LIBCUDACXX_END_NAMESPACE_CPO + +inline namespace __cpo +{ +_LIBCUDACXX_CPO_ACCESSIBILITY auto ssize = __ssize::__fn{}; +} // namespace __cpo + +#endif // _CCCL_STD_VER >= 2017 && !_LIBCUDACXX_COMPILER_MSVC_2017 + +_LIBCUDACXX_END_NAMESPACE_RANGES + +#endif // _LIBCUDACXX___RANGES_SIZE_H diff --git a/libcudacxx/include/cuda/std/detail/libcxx/include/ranges b/libcudacxx/include/cuda/std/detail/libcxx/include/ranges index 56eb793c009..bf79d6d9b2f 100644 --- a/libcudacxx/include/cuda/std/detail/libcxx/include/ranges +++ b/libcudacxx/include/cuda/std/detail/libcxx/include/ranges @@ -317,6 +317,7 @@ namespace std { #include "__ranges/enable_view.h" #include "__ranges/rbegin.h" #include "__ranges/rend.h" +#include "__ranges/size.h" // standard-mandated includes #include "version" diff --git a/libcudacxx/libcxx/test/std/ranges/range.access/size.pass.cpp b/libcudacxx/libcxx/test/std/ranges/range.access/size.pass.cpp new file mode 100644 index 00000000000..3cb9c153d22 --- /dev/null +++ b/libcudacxx/libcxx/test/std/ranges/range.access/size.pass.cpp @@ -0,0 +1,336 @@ +//===----------------------------------------------------------------------===// +// +// 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::size + +#include + +#include +#include +#include "test_macros.h" +#include "test_iterators.h" + +using RangeSizeT = decltype(std::ranges::size); + +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); + +extern Incomplete array_of_incomplete[42]; +static_assert(std::ranges::size(array_of_incomplete) == 42); +static_assert(std::ranges::size(std::move(array_of_incomplete)) == 42); +static_assert(std::ranges::size(std::as_const(array_of_incomplete)) == 42); +static_assert(std::ranges::size(static_cast(array_of_incomplete)) == 42); + +struct SizeMember { + constexpr std::size_t size() { return 42; } +}; + +struct StaticSizeMember { + constexpr static std::size_t size() { return 42; } +}; + +static_assert(!std::is_invocable_v); + +struct SizeFunction { + friend constexpr std::size_t size(SizeFunction) { return 42; } +}; + +// Make sure the size member is preferred. +struct SizeMemberAndFunction { + constexpr std::size_t size() { return 42; } + friend constexpr std::size_t size(SizeMemberAndFunction) { return 0; } +}; + +bool constexpr testArrayType() { + int a[4]; + int b[1]; + SizeMember c[4]; + SizeFunction d[4]; + + assert(std::ranges::size(a) == 4); + ASSERT_SAME_TYPE(decltype(std::ranges::size(a)), std::size_t); + assert(std::ranges::size(b) == 1); + ASSERT_SAME_TYPE(decltype(std::ranges::size(b)), std::size_t); + assert(std::ranges::size(c) == 4); + ASSERT_SAME_TYPE(decltype(std::ranges::size(c)), std::size_t); + assert(std::ranges::size(d) == 4); + ASSERT_SAME_TYPE(decltype(std::ranges::size(d)), std::size_t); + + return true; +} + +struct SizeMemberConst { + constexpr std::size_t size() const { return 42; } +}; + +struct SizeMemberSigned { + constexpr long size() { return 42; } +}; + +bool constexpr testHasSizeMember() { + assert(std::ranges::size(SizeMember()) == 42); + ASSERT_SAME_TYPE(decltype(std::ranges::size(SizeMember())), std::size_t); + + const SizeMemberConst sizeMemberConst; + assert(std::ranges::size(sizeMemberConst) == 42); + + assert(std::ranges::size(SizeMemberAndFunction()) == 42); + + assert(std::ranges::size(SizeMemberSigned()) == 42); + ASSERT_SAME_TYPE(decltype(std::ranges::size(SizeMemberSigned())), long); + + assert(std::ranges::size(StaticSizeMember()) == 42); + ASSERT_SAME_TYPE(decltype(std::ranges::size(StaticSizeMember())), std::size_t); + + return true; +} + +struct MoveOnlySizeFunction { + MoveOnlySizeFunction() = default; + MoveOnlySizeFunction(MoveOnlySizeFunction &&) = default; + MoveOnlySizeFunction(MoveOnlySizeFunction const&) = delete; + + friend constexpr std::size_t size(MoveOnlySizeFunction) { return 42; } +}; + +enum EnumSizeFunction { + a, b +}; + +constexpr std::size_t size(EnumSizeFunction) { return 42; } + +struct SizeFunctionConst { + friend constexpr std::size_t size(const SizeFunctionConst) { return 42; } +}; + +struct SizeFunctionRef { + friend constexpr std::size_t size(SizeFunctionRef&) { return 42; } +}; + +struct SizeFunctionConstRef { + friend constexpr std::size_t size(SizeFunctionConstRef const&) { return 42; } +}; + +struct SizeFunctionSigned { + friend constexpr long size(SizeFunctionSigned) { return 42; } +}; + +bool constexpr testHasSizeFunction() { + assert(std::ranges::size(SizeFunction()) == 42); + ASSERT_SAME_TYPE(decltype(std::ranges::size(SizeFunction())), std::size_t); + static_assert(!std::is_invocable_v); + assert(std::ranges::size(EnumSizeFunction()) == 42); + assert(std::ranges::size(SizeFunctionConst()) == 42); + + SizeFunctionRef a; + assert(std::ranges::size(a) == 42); + + const SizeFunctionConstRef b; + assert(std::ranges::size(b) == 42); + + assert(std::ranges::size(SizeFunctionSigned()) == 42); + ASSERT_SAME_TYPE(decltype(std::ranges::size(SizeFunctionSigned())), long); + + return true; +} + +struct Empty { }; +static_assert(!std::is_invocable_v); + +struct InvalidReturnTypeMember { + Empty size(); +}; + +struct InvalidReturnTypeFunction { + friend Empty size(InvalidReturnTypeFunction); +}; + +struct Convertible { + operator std::size_t(); +}; + +struct ConvertibleReturnTypeMember { + Convertible size(); +}; + +struct ConvertibleReturnTypeFunction { + friend Convertible size(ConvertibleReturnTypeFunction); +}; + +struct BoolReturnTypeMember { + bool size() const; +}; + +struct BoolReturnTypeFunction { + friend bool size(BoolReturnTypeFunction const&); +}; + +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 SizeMemberDisabled { + std::size_t size() { return 42; } +}; + +template <> +inline constexpr bool std::ranges::disable_sized_range = true; + +struct ImproperlyDisabledMember { + std::size_t size() const { return 42; } +}; + +// Intentionally disabling "const ConstSizeMemberDisabled". This doesn't disable anything +// because T is always uncvrefed before being checked. +template <> +inline constexpr bool std::ranges::disable_sized_range = true; + +struct SizeFunctionDisabled { + friend std::size_t size(SizeFunctionDisabled) { return 42; } +}; + +template <> +inline constexpr bool std::ranges::disable_sized_range = true; + +struct ImproperlyDisabledFunction { + friend std::size_t size(ImproperlyDisabledFunction const&) { return 42; } +}; + +template <> +inline constexpr bool std::ranges::disable_sized_range = true; + +static_assert( std::is_invocable_v); +static_assert( std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert( std::is_invocable_v); + +// No begin end. +struct HasMinusOperator { + friend constexpr std::size_t operator-(HasMinusOperator, HasMinusOperator) { return 2; } +}; +static_assert(!std::is_invocable_v); + +struct HasMinusBeginEnd { + struct sentinel { + friend bool operator==(sentinel, forward_iterator); + friend constexpr std::ptrdiff_t operator-(const sentinel, const forward_iterator) { return 2; } + friend constexpr std::ptrdiff_t operator-(const forward_iterator, const sentinel) { return 2; } + }; + + friend constexpr forward_iterator begin(HasMinusBeginEnd) { return {}; } + friend constexpr sentinel end(HasMinusBeginEnd) { return {}; } +}; + +struct other_forward_iterator : forward_iterator { }; + +struct InvalidMinusBeginEnd { + struct sentinel { + friend bool operator==(sentinel, other_forward_iterator); + friend constexpr std::ptrdiff_t operator-(const sentinel, const other_forward_iterator) { return 2; } + friend constexpr std::ptrdiff_t operator-(const other_forward_iterator, const sentinel) { return 2; } + }; + + friend constexpr other_forward_iterator begin(InvalidMinusBeginEnd) { return {}; } + friend constexpr sentinel end(InvalidMinusBeginEnd) { return {}; } +}; + +// short is integer-like, but it is not other_forward_iterator's difference_type. +static_assert(!std::same_as); +static_assert(!std::is_invocable_v); + +struct RandomAccessRange { + struct sentinel { + friend bool operator==(sentinel, random_access_iterator); + friend constexpr std::ptrdiff_t operator-(const sentinel, const random_access_iterator) { return 2; } + friend constexpr std::ptrdiff_t operator-(const random_access_iterator, const sentinel) { return 2; } + }; + + constexpr random_access_iterator begin() { return {}; } + constexpr sentinel end() { return {}; } +}; + +struct IntPtrBeginAndEnd { + int buff[8]; + constexpr int* begin() { return buff; } + constexpr int* end() { return buff + 8; } +}; + +struct DisabledSizeRangeWithBeginEnd { + int buff[8]; + constexpr int* begin() { return buff; } + constexpr int* end() { return buff + 8; } + constexpr std::size_t size() { return 1; } +}; + +template <> +inline constexpr bool std::ranges::disable_sized_range = true; + +struct SizeBeginAndEndMembers { + int buff[8]; + constexpr int* begin() { return buff; } + constexpr int* end() { return buff + 8; } + constexpr std::size_t size() { return 1; } +}; + +constexpr bool testRanges() { + HasMinusBeginEnd a; + assert(std::ranges::size(a) == 2); + // Ensure that this is converted to an *unsigned* type. + ASSERT_SAME_TYPE(decltype(std::ranges::size(a)), std::size_t); + + IntPtrBeginAndEnd b; + assert(std::ranges::size(b) == 8); + + DisabledSizeRangeWithBeginEnd c; + assert(std::ranges::size(c) == 8); + + RandomAccessRange d; + assert(std::ranges::size(d) == 2); + ASSERT_SAME_TYPE(decltype(std::ranges::size(d)), std::size_t); + + SizeBeginAndEndMembers e; + assert(std::ranges::size(e) == 1); + + return true; +} + +// Test ADL-proofing. +struct Incomplete; +template struct Holder { T t; }; +static_assert(!std::is_invocable_v*>); +static_assert(!std::is_invocable_v*&>); + +int main(int, char**) { + testArrayType(); + static_assert(testArrayType()); + + testHasSizeMember(); + static_assert(testHasSizeMember()); + + testHasSizeFunction(); + static_assert(testHasSizeFunction()); + + testRanges(); + static_assert(testRanges()); + + return 0; +} diff --git a/libcudacxx/libcxx/test/std/ranges/range.access/size.verify.cpp b/libcudacxx/libcxx/test/std/ranges/range.access/size.verify.cpp new file mode 100644 index 00000000000..2ee2c1af9bd --- /dev/null +++ b/libcudacxx/libcxx/test/std/ranges/range.access/size.verify.cpp @@ -0,0 +1,21 @@ +//===----------------------------------------------------------------------===// +// +// 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::size + +#include + +extern int arr[]; + +// Verify that for an array of unknown bound `ranges::size` is ill-formed. +void test() { + std::ranges::size(arr); + // expected-error-re@-1 {{{{no matching function for call to object of type 'const (std::ranges::)?__size::__fn'}}}} +} diff --git a/libcudacxx/libcxx/test/std/ranges/range.access/ssize.pass.cpp b/libcudacxx/libcxx/test/std/ranges/range.access/ssize.pass.cpp new file mode 100644 index 00000000000..ac2c5b7b6b7 --- /dev/null +++ b/libcudacxx/libcxx/test/std/ranges/range.access/ssize.pass.cpp @@ -0,0 +1,90 @@ +//===----------------------------------------------------------------------===// +// +// 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::ssize + +#include + +#include +#include "test_macros.h" +#include "test_iterators.h" + +using RangeSSizeT = decltype(std::ranges::ssize); + +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 SizeMember { + constexpr std::size_t size() { return 42; } +}; +static_assert(!std::is_invocable_v); + +struct SizeFunction { + friend constexpr std::size_t size(SizeFunction) { return 42; } +}; + +struct SizeFunctionSigned { + friend constexpr std::ptrdiff_t size(SizeFunctionSigned) { return 42; } +}; + +struct SizedSentinelRange { + int data_[2] = {}; + constexpr int *begin() { return data_; } + constexpr auto end() { return sized_sentinel(data_ + 2); } +}; + +struct ShortUnsignedReturnType { + constexpr unsigned short size() { return 42; } +}; + +// size_t changes depending on the platform. +using SignedSizeT = std::make_signed_t; + +constexpr bool test() { + int a[4]; + + assert(std::ranges::ssize(a) == 4); + ASSERT_SAME_TYPE(decltype(std::ranges::ssize(a)), SignedSizeT); + + assert(std::ranges::ssize(SizeMember()) == 42); + ASSERT_SAME_TYPE(decltype(std::ranges::ssize(SizeMember())), SignedSizeT); + + assert(std::ranges::ssize(SizeFunction()) == 42); + ASSERT_SAME_TYPE(decltype(std::ranges::ssize(SizeFunction())), SignedSizeT); + + assert(std::ranges::ssize(SizeFunctionSigned()) == 42); + ASSERT_SAME_TYPE(decltype(std::ranges::ssize(SizeFunctionSigned())), std::ptrdiff_t); + + SizedSentinelRange b; + assert(std::ranges::ssize(b) == 2); + ASSERT_SAME_TYPE(decltype(std::ranges::ssize(b)), std::ptrdiff_t); + + // This gets converted to ptrdiff_t because it's wider. + ShortUnsignedReturnType c; + assert(std::ranges::ssize(c) == 42); + ASSERT_SAME_TYPE(decltype(std::ranges::ssize(c)), std::ptrdiff_t); + + return true; +} + +// Test ADL-proofing. +struct Incomplete; +template struct Holder { T t; }; +static_assert(!std::is_invocable_v*>); +static_assert(!std::is_invocable_v*&>); + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcudacxx/libcxx/test/std/ranges/range.access/ssize.verify.cpp b/libcudacxx/libcxx/test/std/ranges/range.access/ssize.verify.cpp new file mode 100644 index 00000000000..9885d82c92c --- /dev/null +++ b/libcudacxx/libcxx/test/std/ranges/range.access/ssize.verify.cpp @@ -0,0 +1,21 @@ +//===----------------------------------------------------------------------===// +// +// 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::size + +#include + +extern int arr[]; + +// Verify that for an array of unknown bound `ranges::ssize` is ill-formed. +void test() { + std::ranges::ssize(arr); + // expected-error-re@-1 {{{{no matching function for call to object of type 'const (std::ranges::)?__ssize::__fn'}}}} +} diff --git a/libcudacxx/test/libcudacxx/std/ranges/range.access/size.pass.cpp b/libcudacxx/test/libcudacxx/std/ranges/range.access/size.pass.cpp new file mode 100644 index 00000000000..d4a9650dd32 --- /dev/null +++ b/libcudacxx/test/libcudacxx/std/ranges/range.access/size.pass.cpp @@ -0,0 +1,365 @@ +//===----------------------------------------------------------------------===// +// +// 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::size + +#include + +#include +#include "test_macros.h" +#include "test_iterators.h" + +using RangeSizeT = decltype(cuda::std::ranges::size); + +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); + +#ifndef TEST_COMPILER_NVRTC +extern Incomplete array_of_incomplete[42]; +static_assert(cuda::std::ranges::size(array_of_incomplete) == 42); +static_assert(cuda::std::ranges::size(cuda::std::move(array_of_incomplete)) == 42); + +extern const Incomplete const_array_of_incomplete[42]; +static_assert(cuda::std::ranges::size(const_array_of_incomplete) == 42); +static_assert(cuda::std::ranges::size(static_cast(array_of_incomplete)) == 42); +#endif // !TEST_COMPILER_NVRTC + +struct SizeMember { + __host__ __device__ constexpr size_t size() { return 42; } +}; + +struct StaticSizeMember { + __host__ __device__ constexpr static size_t size() { return 42; } +}; + +static_assert(!cuda::std::is_invocable_v); + +struct SizeFunction { + __host__ __device__ friend constexpr size_t size(SizeFunction) { return 42; } +}; + +// Make sure the size member is preferred. +struct SizeMemberAndFunction { + __host__ __device__ constexpr size_t size() { return 42; } + __host__ __device__ friend constexpr size_t size(SizeMemberAndFunction) { return 0; } +}; + +__host__ __device__ bool constexpr testArrayType() { + int a[4] = {}; + int b[1] = {}; + SizeMember c[4] = {}; + SizeFunction d[4] = {}; + + assert(cuda::std::ranges::size(a) == 4); + ASSERT_SAME_TYPE(decltype(cuda::std::ranges::size(a)), size_t); + assert(cuda::std::ranges::size(b) == 1); + ASSERT_SAME_TYPE(decltype(cuda::std::ranges::size(b)), size_t); + assert(cuda::std::ranges::size(c) == 4); + ASSERT_SAME_TYPE(decltype(cuda::std::ranges::size(c)), size_t); + assert(cuda::std::ranges::size(d) == 4); + ASSERT_SAME_TYPE(decltype(cuda::std::ranges::size(d)), size_t); + + return true; +} + +struct SizeMemberConst { + __host__ __device__ constexpr size_t size() const { return 42; } +}; + +struct SizeMemberSigned { + __host__ __device__ constexpr long size() { return 42; } +}; + +__host__ __device__ bool constexpr testHasSizeMember() { + assert(cuda::std::ranges::size(SizeMember()) == 42); + ASSERT_SAME_TYPE(decltype(cuda::std::ranges::size(SizeMember())), size_t); + + const SizeMemberConst sizeMemberConst{}; + assert(cuda::std::ranges::size(sizeMemberConst) == 42); + + assert(cuda::std::ranges::size(SizeMemberAndFunction()) == 42); + + assert(cuda::std::ranges::size(SizeMemberSigned()) == 42); + ASSERT_SAME_TYPE(decltype(cuda::std::ranges::size(SizeMemberSigned())), long); + + assert(cuda::std::ranges::size(StaticSizeMember()) == 42); + ASSERT_SAME_TYPE(decltype(cuda::std::ranges::size(StaticSizeMember())), size_t); + + return true; +} + +struct MoveOnlySizeFunction { + MoveOnlySizeFunction() = default; + MoveOnlySizeFunction(MoveOnlySizeFunction &&) = default; + MoveOnlySizeFunction(MoveOnlySizeFunction const&) = delete; + + __host__ __device__ friend constexpr size_t size(MoveOnlySizeFunction) { return 42; } +}; + +enum EnumSizeFunction { + a, b +}; + +__host__ __device__ constexpr size_t size(EnumSizeFunction) { return 42; } + +struct SizeFunctionConst { + __host__ __device__ friend constexpr size_t size(const SizeFunctionConst) { return 42; } +}; + +struct SizeFunctionRef { + __host__ __device__ friend constexpr size_t size(SizeFunctionRef&) { return 42; } +}; + +struct SizeFunctionConstRef { + __host__ __device__ friend constexpr size_t size(SizeFunctionConstRef const&) { return 42; } +}; + +struct SizeFunctionSigned { + __host__ __device__ friend constexpr long size(SizeFunctionSigned) { return 42; } +}; + +__host__ __device__ bool constexpr testHasSizeFunction() { + assert(cuda::std::ranges::size(SizeFunction()) == 42); + ASSERT_SAME_TYPE(decltype(cuda::std::ranges::size(SizeFunction())), size_t); + static_assert(!cuda::std::is_invocable_v); + assert(cuda::std::ranges::size(EnumSizeFunction()) == 42); + assert(cuda::std::ranges::size(SizeFunctionConst()) == 42); + + SizeFunctionRef a{}; + assert(cuda::std::ranges::size(a) == 42); + + const SizeFunctionConstRef b{}; + assert(cuda::std::ranges::size(b) == 42); + + assert(cuda::std::ranges::size(SizeFunctionSigned()) == 42); + ASSERT_SAME_TYPE(decltype(cuda::std::ranges::size(SizeFunctionSigned())), long); + + return true; +} + +struct Empty { }; +static_assert(!cuda::std::is_invocable_v); + +struct InvalidReturnTypeMember { + __host__ __device__ Empty size(); +}; + +struct InvalidReturnTypeFunction { + __host__ __device__ friend Empty size(InvalidReturnTypeFunction); +}; + +struct Convertible { + __host__ __device__ operator size_t(); +}; + +struct ConvertibleReturnTypeMember { + __host__ __device__ Convertible size(); +}; + +struct ConvertibleReturnTypeFunction { + __host__ __device__ friend Convertible size(ConvertibleReturnTypeFunction); +}; + +struct BoolReturnTypeMember { + __host__ __device__ bool size() const; +}; + +struct BoolReturnTypeFunction { + __host__ __device__ friend bool size(BoolReturnTypeFunction const&); +}; + +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 SizeMemberDisabled { + __host__ __device__ size_t size() { return 42; } +}; + +namespace cuda { namespace std { namespace ranges { +template <> +_LIBCUDACXX_INLINE_VAR constexpr bool disable_sized_range = true; +}}} // namespace cuda::std::ranges +struct ImproperlyDisabledMember { + __host__ __device__ size_t size() const { return 42; } +}; + +// Intentionally disabling "const ConstSizeMemberDisabled". This doesn't disable anything +// because T is always uncvrefed before being checked. +namespace cuda { namespace std { namespace ranges { +template <> +_LIBCUDACXX_INLINE_VAR constexpr bool disable_sized_range = true; +}}} // namespace cuda::std::ranges + +struct SizeFunctionDisabled { + __host__ __device__ friend size_t size(SizeFunctionDisabled) { return 42; } +}; + +namespace cuda { namespace std { namespace ranges { +template <> +_LIBCUDACXX_INLINE_VAR constexpr bool disable_sized_range = true; +}}} // namespace cuda::std::ranges + +struct ImproperlyDisabledFunction { + __host__ __device__ friend size_t size(ImproperlyDisabledFunction const&) { return 42; } +}; + +namespace cuda { namespace std { namespace ranges { +template <> +_LIBCUDACXX_INLINE_VAR constexpr bool disable_sized_range = true; +}}} // namespace cuda::std::ranges + +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); + +// No begin end. +struct HasMinusOperator { + __host__ __device__ friend constexpr size_t operator-(HasMinusOperator, HasMinusOperator) { return 2; } +}; +static_assert(!cuda::std::is_invocable_v); + +struct HasMinusBeginEnd { + struct sentinel { + __host__ __device__ friend bool operator==(sentinel, forward_iterator); +#if TEST_STD_VER < 2020 + __host__ __device__ friend bool operator==(forward_iterator, sentinel); + __host__ __device__ friend bool operator!=(sentinel, forward_iterator); + __host__ __device__ friend bool operator!=(forward_iterator, sentinel); +#endif + __host__ __device__ friend constexpr cuda::std::ptrdiff_t operator-(const sentinel, const forward_iterator) { return 2; } + __host__ __device__ friend constexpr cuda::std::ptrdiff_t operator-(const forward_iterator, const sentinel) { return 2; } + }; + + __host__ __device__ friend constexpr forward_iterator begin(HasMinusBeginEnd) { return {}; } + __host__ __device__ friend constexpr sentinel end(HasMinusBeginEnd) { return {}; } +}; + +struct other_forward_iterator : forward_iterator { }; + +struct InvalidMinusBeginEnd { + struct sentinel { + __host__ __device__ friend bool operator==(sentinel, other_forward_iterator); +#if TEST_STD_VER < 2020 + __host__ __device__ friend bool operator==(other_forward_iterator, sentinel); + __host__ __device__ friend bool operator!=(sentinel, other_forward_iterator); + __host__ __device__ friend bool operator!=(other_forward_iterator, sentinel); +#endif + __host__ __device__ friend constexpr cuda::std::ptrdiff_t operator-(const sentinel, const other_forward_iterator) { return 2; } + __host__ __device__ friend constexpr cuda::std::ptrdiff_t operator-(const other_forward_iterator, const sentinel) { return 2; } + }; + + __host__ __device__ friend constexpr other_forward_iterator begin(InvalidMinusBeginEnd) { return {}; } + __host__ __device__ friend constexpr sentinel end(InvalidMinusBeginEnd) { return {}; } +}; + +// short is integer-like, but it is not other_forward_iterator's difference_type. +static_assert(!cuda::std::same_as); +static_assert(!cuda::std::is_invocable_v); + +struct RandomAccessRange { + struct sentinel { + __host__ __device__ friend bool operator==(sentinel, random_access_iterator); +#if TEST_STD_VER < 2020 + __host__ __device__ friend bool operator==(random_access_iterator, sentinel); + __host__ __device__ friend bool operator!=(sentinel, random_access_iterator); + __host__ __device__ friend bool operator!=(random_access_iterator, sentinel); +#endif + __host__ __device__ friend constexpr cuda::std::ptrdiff_t operator-(const sentinel, const random_access_iterator) { return 2; } + __host__ __device__ friend constexpr cuda::std::ptrdiff_t operator-(const random_access_iterator, const sentinel) { return 2; } + }; + + __host__ __device__ constexpr random_access_iterator begin() { return {}; } + __host__ __device__ constexpr sentinel end() { return {}; } +}; + +struct IntPtrBeginAndEnd { + int buff[8]; + __host__ __device__ constexpr int* begin() { return buff; } + __host__ __device__ constexpr int* end() { return buff + 8; } +}; + +struct DisabledSizeRangeWithBeginEnd { + int buff[8]; + __host__ __device__ constexpr int* begin() { return buff; } + __host__ __device__ constexpr int* end() { return buff + 8; } + __host__ __device__ constexpr size_t size() { return 1; } +}; + +template <> +inline constexpr bool cuda::std::ranges::disable_sized_range = true; + +struct SizeBeginAndEndMembers { + int buff[8]; + __host__ __device__ constexpr int* begin() { return buff; } + __host__ __device__ constexpr int* end() { return buff + 8; } + __host__ __device__ constexpr size_t size() { return 1; } +}; + +__host__ __device__ constexpr bool testRanges() { + HasMinusBeginEnd a{}; + assert(cuda::std::ranges::size(a) == 2); + // Ensure that this is converted to an *unsigned* type. + ASSERT_SAME_TYPE(decltype(cuda::std::ranges::size(a)), size_t); + + IntPtrBeginAndEnd b{}; + assert(cuda::std::ranges::size(b) == 8); + + DisabledSizeRangeWithBeginEnd c{}; + assert(cuda::std::ranges::size(c) == 8); + + RandomAccessRange d{}; + assert(cuda::std::ranges::size(d) == 2); + ASSERT_SAME_TYPE(decltype(cuda::std::ranges::size(d)), size_t); + + SizeBeginAndEndMembers e{}; + assert(cuda::std::ranges::size(e) == 1); + + return true; +} + +#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*&>); +#endif // TEST_STD_VER > 2017 + +int main(int, char**) { + testArrayType(); + static_assert(testArrayType()); + + testHasSizeMember(); + static_assert(testHasSizeMember()); + + testHasSizeFunction(); + static_assert(testHasSizeFunction()); + + testRanges(); + static_assert(testRanges()); + + return 0; +} diff --git a/libcudacxx/test/libcudacxx/std/ranges/range.access/size.verify.cpp b/libcudacxx/test/libcudacxx/std/ranges/range.access/size.verify.cpp new file mode 100644 index 00000000000..0cd4cbbfa45 --- /dev/null +++ b/libcudacxx/test/libcudacxx/std/ranges/range.access/size.verify.cpp @@ -0,0 +1,24 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// std::ranges::size + +#include + +extern int arr[]; + +// Verify that for an array of unknown bound `ranges::size` is ill-formed. +void test() { + cuda::std::ranges::size(arr); + // expected-error-re@-1 {{{{no matching function for call to object of type 'const (std::ranges::)?__size::__fn'}}}} +} diff --git a/libcudacxx/test/libcudacxx/std/ranges/range.access/ssize.pass.cpp b/libcudacxx/test/libcudacxx/std/ranges/range.access/ssize.pass.cpp new file mode 100644 index 00000000000..c8cefb2b236 --- /dev/null +++ b/libcudacxx/test/libcudacxx/std/ranges/range.access/ssize.pass.cpp @@ -0,0 +1,94 @@ +//===----------------------------------------------------------------------===// +// +// 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::ssize + +#include + +#include +#include "test_macros.h" +#include "test_iterators.h" + +using RangeSSizeT = decltype(cuda::std::ranges::ssize); + +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 SizeMember { + __host__ __device__ constexpr size_t size() { return 42; } +}; +static_assert(!cuda::std::is_invocable_v); + +struct SizeFunction { + __host__ __device__ friend constexpr size_t size(SizeFunction) { return 42; } +}; + +struct SizeFunctionSigned { + __host__ __device__ friend constexpr cuda::std::ptrdiff_t size(SizeFunctionSigned) { return 42; } +}; + +struct SizedSentinelRange { + int data_[2] = {}; + __host__ __device__ constexpr int *begin() { return data_; } + __host__ __device__ constexpr auto end() { return sized_sentinel(data_ + 2); } +}; + +struct ShortUnsignedReturnType { + __host__ __device__ constexpr unsigned short size() { return 42; } +}; + +// size_t changes depending on the platform. +using SignedSizeT = cuda::std::make_signed_t; + +__host__ __device__ constexpr bool test() { + int a[4] = {}; + + assert(cuda::std::ranges::ssize(a) == 4); + ASSERT_SAME_TYPE(decltype(cuda::std::ranges::ssize(a)), SignedSizeT); + + assert(cuda::std::ranges::ssize(SizeMember()) == 42); + ASSERT_SAME_TYPE(decltype(cuda::std::ranges::ssize(SizeMember())), SignedSizeT); + + assert(cuda::std::ranges::ssize(SizeFunction()) == 42); + ASSERT_SAME_TYPE(decltype(cuda::std::ranges::ssize(SizeFunction())), SignedSizeT); + + assert(cuda::std::ranges::ssize(SizeFunctionSigned()) == 42); + ASSERT_SAME_TYPE(decltype(cuda::std::ranges::ssize(SizeFunctionSigned())), cuda::std::ptrdiff_t); + + SizedSentinelRange b{}; + assert(cuda::std::ranges::ssize(b) == 2); + ASSERT_SAME_TYPE(decltype(cuda::std::ranges::ssize(b)), cuda::std::ptrdiff_t); + + // This gets converted to ptrdiff_t because it's wider. + ShortUnsignedReturnType c{}; + assert(cuda::std::ranges::ssize(c) == 42); + ASSERT_SAME_TYPE(decltype(cuda::std::ranges::ssize(c)), ptrdiff_t); + + return true; +} + +#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*&>); +#endif // TEST_STD_VER > 2017 + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcudacxx/test/libcudacxx/std/ranges/range.access/ssize.verify.cpp b/libcudacxx/test/libcudacxx/std/ranges/range.access/ssize.verify.cpp new file mode 100644 index 00000000000..1134f182b39 --- /dev/null +++ b/libcudacxx/test/libcudacxx/std/ranges/range.access/ssize.verify.cpp @@ -0,0 +1,24 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// std::ranges::size + +#include + +extern int arr[]; + +// Verify that for an array of unknown bound `ranges::ssize` is ill-formed. +void test() { + cuda::std::ranges::ssize(arr); + // expected-error-re@-1 {{{{no matching function for call to object of type 'const (std::ranges::)?__ssize::__fn'}}}} +}