diff --git a/libcxx/include/__ranges/size.h b/libcxx/include/__ranges/size.h --- a/libcxx/include/__ranges/size.h +++ b/libcxx/include/__ranges/size.h @@ -98,6 +98,26 @@ inline namespace __cpo { inline constexpr auto size = __size::__fn{}; } // namespace __cpo + +namespace __ssize { + struct __fn { + template + requires requires (_Tp&& __t) { ranges::size(__t); } + [[nodiscard]] constexpr integral auto operator()(_Tp&& __t) const + noexcept(noexcept(ranges::size(__t))) { + using _Signed = make_signed_t; + if constexpr (sizeof(ptrdiff_t) > sizeof(_Signed)) + return static_cast(ranges::size(__t)); + else + return static_cast<_Signed>(ranges::size(__t)); + } + }; +} + +inline namespace __cpo { + inline constexpr const auto ssize = __ssize::__fn{}; +} // namespace __cpo + } // namespace ranges // clang-format off diff --git a/libcxx/test/std/ranges/range.access/range.prim/size.pass.cpp b/libcxx/test/std/ranges/range.access/range.prim/size.pass.cpp --- a/libcxx/test/std/ranges/range.access/range.prim/size.pass.cpp +++ b/libcxx/test/std/ranges/range.access/range.prim/size.pass.cpp @@ -9,7 +9,6 @@ // UNSUPPORTED: c++03, c++11, c++14, c++17 // UNSUPPORTED: libcpp-no-concepts // UNSUPPORTED: gcc-10 -// XFAIL: msvc && clang // std::ranges::size @@ -51,9 +50,13 @@ SizeFunction d[4]; assert(std::ranges::size(a) == 4); + ASSERT_SAME_TYPE(decltype(std::ranges::size(a)), size_t); assert(std::ranges::size(b) == 1); + ASSERT_SAME_TYPE(decltype(std::ranges::size(b)), size_t); assert(std::ranges::size(c) == 4); + ASSERT_SAME_TYPE(decltype(std::ranges::size(c)), size_t); assert(std::ranges::size(d) == 4); + ASSERT_SAME_TYPE(decltype(std::ranges::size(d)), size_t); return true; } @@ -68,6 +71,7 @@ bool constexpr testHasSizeMember() { assert(std::ranges::size(SizeMember()) == 42); + ASSERT_SAME_TYPE(decltype(std::ranges::size(SizeMember())), size_t); const SizeMemberConst sizeMemberConst; assert(std::ranges::size(sizeMemberConst) == 42); @@ -112,6 +116,7 @@ bool constexpr testHasSizeFunction() { assert(std::ranges::size(SizeFunction()) == 42); + ASSERT_SAME_TYPE(decltype(std::ranges::size(SizeFunction())), size_t); assert(std::ranges::size(MoveOnlySizeFunction()) == 42); assert(std::ranges::size(EnumSizeFunction()) == 42); assert(std::ranges::size(SizeFunctionConst()) == 42); @@ -214,12 +219,13 @@ }; struct HasMinusBeginEnd { + using difference_type = std::iter_difference_t>; friend constexpr forward_iterator begin(HasMinusBeginEnd) { return {}; } friend constexpr sentinel end(HasMinusBeginEnd) { return {}; } }; -constexpr long operator-(const sentinel, const forward_iterator) { return 2; } -constexpr long operator-(const forward_iterator, const sentinel) { return 2; } +constexpr HasMinusBeginEnd::difference_type operator-(const sentinel, const forward_iterator) { return 2; } +constexpr HasMinusBeginEnd::difference_type operator-(const forward_iterator, const sentinel) { return 2; } struct other_forward_iterator : forward_iterator { }; @@ -278,6 +284,40 @@ return true; } +struct ShortUnsignedReturnType { + constexpr unsigned short size() { return 42; } +}; + +// size_t changes depending on the platform. +using SignedSizeT = std::make_signed_t; + +constexpr bool testSsize() { + 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())), long); + + RandomAccesslRange b; + assert(std::ranges::ssize(b) == 2); + ASSERT_SAME_TYPE(decltype(std::ranges::ssize(b)), long); + + // 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)), ptrdiff_t); + + return true; +} + int main(int, char**) { testArrayType(); static_assert(testArrayType()); @@ -291,5 +331,8 @@ testRanges(); static_assert(testRanges()); + testSsize(); + static_assert(testSsize()); + return 0; } diff --git a/libcxx/test/std/ranges/range.access/range.prim/ssize.pass.cpp b/libcxx/test/std/ranges/range.access/range.prim/ssize.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.access/range.prim/ssize.pass.cpp @@ -0,0 +1,95 @@ +//===----------------------------------------------------------------------===// +// +// 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: libcpp-no-concepts +// UNSUPPORTED: gcc-10 + +// 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); + +static_assert(std::semiregular>); + +struct SizeMember { + constexpr size_t size() { return 42; } +}; + +static_assert(!std::is_invocable_v); + +struct SizeFunction { + friend constexpr size_t size(SizeFunction) { return 42; } +}; + +struct SizeFunctionSigned { + friend constexpr std::ptrdiff_t size(SizeFunctionSigned) { return 42; } +}; + +struct sentinel { + bool operator==(std::input_or_output_iterator auto) const { return true; } +}; + +struct RandomAccesslRange { + constexpr random_access_iterator begin() { return {}; } + constexpr sentinel end() { return {}; } +}; + +constexpr std::ptrdiff_t operator-(const sentinel, const random_access_iterator) { return 2; } +constexpr std::ptrdiff_t operator-(const random_access_iterator, const sentinel) { return 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); + + RandomAccesslRange 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)), ptrdiff_t); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +}