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 @@ -111,6 +111,28 @@ inline namespace __cpo { inline constexpr const auto size = __size::__fn{}; } // namespace __cpo + +namespace __ssize { + template + concept __can_invoke_size = invocable<__size::__fn, _Tp>; + + struct __fn { + template<__can_invoke_size _Tp> + [[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 @@ -50,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; } @@ -67,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); @@ -111,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); @@ -267,6 +273,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()); @@ -280,5 +320,8 @@ testRanges(); static_assert(testRanges()); + testSsize(); + static_assert(testSsize()); + return 0; }