diff --git a/libcxx/docs/Status/RangesPaper.csv b/libcxx/docs/Status/RangesPaper.csv --- a/libcxx/docs/Status/RangesPaper.csv +++ b/libcxx/docs/Status/RangesPaper.csv @@ -100,15 +100,15 @@ | `ranges::end `_ | `range::cbegin `_ | `ranges::cend `_ -| ranges::rbegin -| ranges::rend -| ranges::crbegin -| ranges::crend +| `ranges::rbegin `_ +| `ranges::rend `_ +| `ranges::crbegin `_ +| `ranges::crend `_ | `ranges::size `_ | `ranges::ssize `_ | `ranges::empty `_ | `ranges::data `_ -| `ranges::cdata `_",[iterator.concepts],Christopher Di Bella and Zoe Carver,In progress +| `ranges::cdata `_",[iterator.concepts],Various,✅ `[range.range] `_,"| `ranges::range `_ | `ranges::borrowed_range `_ | `ranges::enable_borrowed_range `_ diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -343,7 +343,9 @@ __ranges/non_propagating_cache.h __ranges/owning_view.h __ranges/range_adaptor.h + __ranges/rbegin.h __ranges/ref_view.h + __ranges/rend.h __ranges/reverse_view.h __ranges/single_view.h __ranges/size.h diff --git a/libcxx/include/__concepts/class_or_enum.h b/libcxx/include/__concepts/class_or_enum.h --- a/libcxx/include/__concepts/class_or_enum.h +++ b/libcxx/include/__concepts/class_or_enum.h @@ -26,6 +26,7 @@ concept __class_or_enum = is_class_v<_Tp> || is_union_v<_Tp> || is_enum_v<_Tp>; // Work around Clang bug https://llvm.org/PR52970 +// TODO: remove this workaround once libc++ no longer has to support Clang 13 (it was fixed in Clang 14). template concept __workaround_52970 = is_class_v<__uncvref_t<_Tp>> || is_union_v<__uncvref_t<_Tp>>; diff --git a/libcxx/include/__ranges/access.h b/libcxx/include/__ranges/access.h --- a/libcxx/include/__ranges/access.h +++ b/libcxx/include/__ranges/access.h @@ -14,9 +14,7 @@ #include <__iterator/concepts.h> #include <__iterator/readable_traits.h> #include <__ranges/enable_borrowed_range.h> -#include <__utility/as_const.h> #include <__utility/auto_cast.h> -#include #include #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) @@ -130,8 +128,7 @@ { _LIBCPP_AUTO_CAST(end(__t)) } -> sentinel_for>; }; - class __fn { - public: + struct __fn { template [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp (&__t)[_Np]) const noexcept requires (sizeof(_Tp) != 0) // Disallow incomplete element types. diff --git a/libcxx/include/__ranges/rbegin.h b/libcxx/include/__ranges/rbegin.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__ranges/rbegin.h @@ -0,0 +1,130 @@ +// -*- 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 +// +//===----------------------------------------------------------------------===// +#ifndef _LIBCPP___RANGES_RBEGIN_H +#define _LIBCPP___RANGES_RBEGIN_H + +#include <__concepts/class_or_enum.h> +#include <__concepts/same_as.h> +#include <__config> +#include <__iterator/concepts.h> +#include <__iterator/readable_traits.h> +#include <__iterator/reverse_iterator.h> +#include <__ranges/access.h> +#include <__utility/auto_cast.h> +#include + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if !defined(_LIBCPP_HAS_NO_CONCEPTS) + +// [ranges.access.rbegin] + +namespace ranges { +namespace __rbegin { +template +concept __member_rbegin = + __can_borrow<_Tp> && + __workaround_52970<_Tp> && + requires(_Tp&& __t) { + { _LIBCPP_AUTO_CAST(__t.rbegin()) } -> input_or_output_iterator; + }; + +void rbegin(auto&) = delete; +void rbegin(const auto&) = delete; + +template +concept __unqualified_rbegin = + !__member_rbegin<_Tp> && + __can_borrow<_Tp> && + __class_or_enum> && + requires(_Tp&& __t) { + { _LIBCPP_AUTO_CAST(rbegin(__t)) } -> input_or_output_iterator; + }; + +template +concept __can_reverse = + __can_borrow<_Tp> && + !__member_rbegin<_Tp> && + !__unqualified_rbegin<_Tp> && + requires(_Tp&& __t) { + { ranges::begin(__t) } -> same_as; + { ranges::begin(__t) } -> bidirectional_iterator; + }; + +struct __fn { + template + requires __member_rbegin<_Tp> + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const + noexcept(noexcept(_LIBCPP_AUTO_CAST(__t.rbegin()))) + { + return _LIBCPP_AUTO_CAST(__t.rbegin()); + } + + template + requires __unqualified_rbegin<_Tp> + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const + noexcept(noexcept(_LIBCPP_AUTO_CAST(rbegin(__t)))) + { + return _LIBCPP_AUTO_CAST(rbegin(__t)); + } + + template + requires __can_reverse<_Tp> + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const + noexcept(noexcept(std::make_reverse_iterator(ranges::end(__t)))) + { + return std::make_reverse_iterator(ranges::end(__t)); + } + + void operator()(auto&&) const = delete; +}; +} // namespace __rbegin + +inline namespace __cpo { + inline constexpr auto rbegin = __rbegin::__fn{}; +} // namespace __cpo +} // namespace ranges + +// [range.access.crbegin] + +namespace ranges { +namespace __crbegin { +struct __fn { + template + requires is_lvalue_reference_v<_Tp&&> + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI + constexpr auto operator()(_Tp&& __t) const + noexcept(noexcept(ranges::rbegin(static_cast&>(__t)))) + -> decltype( ranges::rbegin(static_cast&>(__t))) + { return ranges::rbegin(static_cast&>(__t)); } + + template + requires is_rvalue_reference_v<_Tp&&> + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI + constexpr auto operator()(_Tp&& __t) const + noexcept(noexcept(ranges::rbegin(static_cast(__t)))) + -> decltype( ranges::rbegin(static_cast(__t))) + { return ranges::rbegin(static_cast(__t)); } +}; +} // namespace __crbegin + +inline namespace __cpo { + inline constexpr auto crbegin = __crbegin::__fn{}; +} // namespace __cpo +} // namespace ranges + +#endif // !defined(_LIBCPP_HAS_NO_CONCEPTS) + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP___RANGES_RBEGIN_H diff --git a/libcxx/include/__ranges/rend.h b/libcxx/include/__ranges/rend.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__ranges/rend.h @@ -0,0 +1,133 @@ +// -*- 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 +// +//===----------------------------------------------------------------------===// +#ifndef _LIBCPP___RANGES_REND_H +#define _LIBCPP___RANGES_REND_H + +#include <__concepts/class_or_enum.h> +#include <__concepts/same_as.h> +#include <__config> +#include <__iterator/concepts.h> +#include <__iterator/readable_traits.h> +#include <__iterator/reverse_iterator.h> +#include <__ranges/access.h> +#include <__ranges/rbegin.h> +#include <__utility/auto_cast.h> +#include + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if !defined(_LIBCPP_HAS_NO_CONCEPTS) + +// [range.access.rend] + +namespace ranges { +namespace __rend { +template +concept __member_rend = + __can_borrow<_Tp> && + __workaround_52970<_Tp> && + requires(_Tp&& __t) { + ranges::rbegin(__t); + { _LIBCPP_AUTO_CAST(__t.rend()) } -> sentinel_for; + }; + +void rend(auto&) = delete; +void rend(const auto&) = delete; + +template +concept __unqualified_rend = + !__member_rend<_Tp> && + __can_borrow<_Tp> && + __class_or_enum> && + requires(_Tp&& __t) { + ranges::rbegin(__t); + { _LIBCPP_AUTO_CAST(rend(__t)) } -> sentinel_for; + }; + +template +concept __can_reverse = + __can_borrow<_Tp> && + !__member_rend<_Tp> && + !__unqualified_rend<_Tp> && + requires(_Tp&& __t) { + { ranges::begin(__t) } -> same_as; + { ranges::begin(__t) } -> bidirectional_iterator; + }; + +struct __fn { + template + requires __member_rend<_Tp> + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const + noexcept(noexcept(_LIBCPP_AUTO_CAST(__t.rend()))) + { + return _LIBCPP_AUTO_CAST(__t.rend()); + } + + template + requires __unqualified_rend<_Tp> + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const + noexcept(noexcept(_LIBCPP_AUTO_CAST(rend(__t)))) + { + return _LIBCPP_AUTO_CAST(rend(__t)); + } + + template + requires __can_reverse<_Tp> + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const + noexcept(noexcept(std::make_reverse_iterator(ranges::begin(__t)))) + { + return std::make_reverse_iterator(ranges::begin(__t)); + } + + void operator()(auto&&) const = delete; +}; +} // namespace __rend + +inline namespace __cpo { + inline constexpr auto rend = __rend::__fn{}; +} // namespace __cpo +} // namespace ranges + +// [range.access.crend] + +namespace ranges { +namespace __crend { +struct __fn { + template + requires is_lvalue_reference_v<_Tp&&> + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI + constexpr auto operator()(_Tp&& __t) const + noexcept(noexcept(ranges::rend(static_cast&>(__t)))) + -> decltype( ranges::rend(static_cast&>(__t))) + { return ranges::rend(static_cast&>(__t)); } + + template + requires is_rvalue_reference_v<_Tp&&> + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI + constexpr auto operator()(_Tp&& __t) const + noexcept(noexcept(ranges::rend(static_cast(__t)))) + -> decltype( ranges::rend(static_cast(__t))) + { return ranges::rend(static_cast(__t)); } +}; +} // namespace __crend + +inline namespace __cpo { + inline constexpr auto crend = __crend::__fn{}; +} // namespace __cpo +} // namespace ranges + +#endif // !defined(_LIBCPP_HAS_NO_CONCEPTS) + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP___RANGES_REND_H diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap --- a/libcxx/include/module.modulemap +++ b/libcxx/include/module.modulemap @@ -810,7 +810,9 @@ module non_propagating_cache { private header "__ranges/non_propagating_cache.h" } module owning_view { private header "__ranges/owning_view.h" } module range_adaptor { private header "__ranges/range_adaptor.h" } + module rbegin { private header "__ranges/rbegin.h" } module ref_view { private header "__ranges/ref_view.h" } + module rend { private header "__ranges/rend.h" } module reverse_view { private header "__ranges/reverse_view.h" } module single_view { private header "__ranges/single_view.h" } module size { private header "__ranges/size.h" } diff --git a/libcxx/include/ranges b/libcxx/include/ranges --- a/libcxx/include/ranges +++ b/libcxx/include/ranges @@ -250,7 +250,9 @@ #include <__ranges/enable_view.h> #include <__ranges/iota_view.h> #include <__ranges/join_view.h> +#include <__ranges/rbegin.h> #include <__ranges/ref_view.h> +#include <__ranges/rend.h> #include <__ranges/reverse_view.h> #include <__ranges/single_view.h> #include <__ranges/size.h> diff --git a/libcxx/test/libcxx/diagnostics/detail.headers/ranges/rbegin.module.verify.cpp b/libcxx/test/libcxx/diagnostics/detail.headers/ranges/rbegin.module.verify.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/diagnostics/detail.headers/ranges/rbegin.module.verify.cpp @@ -0,0 +1,15 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// REQUIRES: modules-build + +// WARNING: This test was generated by 'generate_private_header_tests.py' +// and should not be edited manually. + +// expected-error@*:* {{use of private header from outside its module: '__ranges/rbegin.h'}} +#include <__ranges/rbegin.h> diff --git a/libcxx/test/libcxx/diagnostics/detail.headers/ranges/rend.module.verify.cpp b/libcxx/test/libcxx/diagnostics/detail.headers/ranges/rend.module.verify.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/diagnostics/detail.headers/ranges/rend.module.verify.cpp @@ -0,0 +1,15 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// REQUIRES: modules-build + +// WARNING: This test was generated by 'generate_private_header_tests.py' +// and should not be edited manually. + +// expected-error@*:* {{use of private header from outside its module: '__ranges/rend.h'}} +#include <__ranges/rend.h> diff --git a/libcxx/test/std/ranges/range.access/begin.pass.cpp b/libcxx/test/std/ranges/range.access/begin.pass.cpp --- a/libcxx/test/std/ranges/range.access/begin.pass.cpp +++ b/libcxx/test/std/ranges/range.access/begin.pass.cpp @@ -16,6 +16,7 @@ #include #include +#include #include "test_macros.h" #include "test_iterators.h" @@ -28,6 +29,10 @@ 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); @@ -105,12 +110,6 @@ return true; } -struct BeginMemberFunction { - int x; - constexpr const int *begin() const { return &x; } - friend int *begin(BeginMemberFunction const&); -}; - struct BeginMemberReturnsInt { int begin() const; }; @@ -127,12 +126,6 @@ }; static_assert(!std::is_invocable_v); -struct EmptyPtrBeginMember { - struct Empty {}; - Empty x; - constexpr const Empty *begin() const { return &x; } -}; - struct PtrConvertibleBeginMember { struct iterator { operator int*() const; }; iterator begin() const; @@ -154,6 +147,18 @@ 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); @@ -193,24 +198,27 @@ static_assert( std::is_invocable_v); static_assert( std::is_invocable_v); -struct BeginFunctionWithDataMember { - int x; - int begin; - friend constexpr const int *begin(BeginFunctionWithDataMember const& bf) { return &bf.x; } +struct BeginFunctionReturnsInt { + friend int begin(BeginFunctionReturnsInt const&); }; +static_assert(!std::is_invocable_v); -struct BeginFunctionWithPrivateBeginMember { - int y; - friend constexpr const int *begin(BeginFunctionWithPrivateBeginMember const& bf) { return &bf.y; } -private: - const int *begin() const; +struct BeginFunctionReturnsVoidPtr { + friend void *begin(BeginFunctionReturnsVoidPtr const&); }; +static_assert(!std::is_invocable_v); -struct BeginFunctionReturnsEmptyPtr { +struct BeginFunctionReturnsEmpty { struct Empty {}; - Empty x; - friend constexpr const Empty *begin(BeginFunctionReturnsEmptyPtr const& bf) { return &bf.x; } + friend Empty begin(BeginFunctionReturnsEmpty 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]; } @@ -223,27 +231,24 @@ template<> inline constexpr bool std::ranges::enable_borrowed_range = true; -struct BeginFunctionReturnsInt { - friend int begin(BeginFunctionReturnsInt const&); -}; -static_assert(!std::is_invocable_v); - -struct BeginFunctionReturnsVoidPtr { - friend void *begin(BeginFunctionReturnsVoidPtr const&); +struct BeginFunctionReturnsEmptyPtr { + struct Empty {}; + Empty x; + friend constexpr const Empty *begin(BeginFunctionReturnsEmptyPtr const& bf) { return &bf.x; } }; -static_assert(!std::is_invocable_v); -struct BeginFunctionReturnsEmpty { - struct Empty {}; - friend Empty begin(BeginFunctionReturnsEmpty const&); +struct BeginFunctionWithDataMember { + int x; + int begin; + friend constexpr const int *begin(BeginFunctionWithDataMember const& bf) { return &bf.x; } }; -static_assert(!std::is_invocable_v); -struct BeginFunctionReturnsPtrConvertible { - struct iterator { operator int*() const; }; - friend iterator begin(BeginFunctionReturnsPtrConvertible const&); +struct BeginFunctionWithPrivateBeginMember { + int y; + friend constexpr const int *begin(BeginFunctionWithPrivateBeginMember const& bf) { return &bf.y; } +private: + const int *begin() const; }; -static_assert(!std::is_invocable_v); constexpr bool testBeginFunction() { BeginFunction a{}; diff --git a/libcxx/test/std/ranges/range.access/end.pass.cpp b/libcxx/test/std/ranges/range.access/end.pass.cpp --- a/libcxx/test/std/ranges/range.access/end.pass.cpp +++ b/libcxx/test/std/ranges/range.access/end.pass.cpp @@ -16,6 +16,7 @@ #include #include +#include #include "test_macros.h" #include "test_iterators.h" @@ -28,6 +29,10 @@ 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); @@ -91,40 +96,18 @@ return 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 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 Empty { }; -struct EmptyEndMember { - Empty begin() const; - Empty end() const; -}; -struct EmptyPtrEndMember { - Empty x; - constexpr const Empty *begin() const { return nullptr; } - constexpr const Empty *end() const { return &x; } -}; - -static_assert(!std::is_invocable_v); - struct PtrConvertible { operator int*() const; }; @@ -132,13 +115,11 @@ 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 { @@ -146,7 +127,6 @@ 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); @@ -160,6 +140,26 @@ 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); @@ -199,74 +199,67 @@ static_assert( std::is_invocable_v); static_assert( std::is_invocable_v); -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 : private EndMember { - int y; - friend constexpr const int *begin(EndFunctionWithPrivateEndMember const&) { return nullptr; } - friend constexpr const int *end(EndFunctionWithPrivateEndMember const& bf) { return &bf.y; } -}; - -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 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 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 : private EndMember { + int y; + friend constexpr const int *begin(EndFunctionWithPrivateEndMember const&) { return nullptr; } + friend constexpr const int *end(EndFunctionWithPrivateEndMember const& bf) { return &bf.y; } +}; + struct BeginMemberEndFunction { int x; constexpr const int *begin() const { return nullptr; } diff --git a/libcxx/test/std/ranges/range.access/rbegin.pass.cpp b/libcxx/test/std/ranges/range.access/rbegin.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.access/rbegin.pass.cpp @@ -0,0 +1,493 @@ +//===----------------------------------------------------------------------===// +// +// 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: libcpp-has-no-incomplete-ranges + +// std::ranges::rbegin +// std::ranges::crbegin + +#include + +#include +#include +#include "test_macros.h" +#include "test_iterators.h" + +using RangeRBeginT = decltype(std::ranges::rbegin); +using RangeCRBeginT = decltype(std::ranges::crbegin); + +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); + +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 RBeginMember { + int x; + constexpr const int *rbegin() 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::rbegin(x)), std::reverse_iterator); + ASSERT_SAME_TYPE(decltype(std::ranges::crbegin(x)), std::reverse_iterator); + } + { + int x[2][2]; + ASSERT_SAME_TYPE(decltype(std::ranges::rbegin(x)), std::reverse_iterator); + ASSERT_SAME_TYPE(decltype(std::ranges::crbegin(x)), std::reverse_iterator); + } + { + struct Different { + char*& rbegin(); + short*& rbegin() const; + } x; + ASSERT_SAME_TYPE(decltype(std::ranges::rbegin(x)), char*); + ASSERT_SAME_TYPE(decltype(std::ranges::crbegin(x)), short*); + } + return true; +} + +constexpr bool testArray() { + int a[2]; + assert(std::ranges::rbegin(a).base() == a + 2); + assert(std::ranges::crbegin(a).base() == a + 2); + + int b[2][2]; + assert(std::ranges::rbegin(b).base() == b + 2); + assert(std::ranges::crbegin(b).base() == b + 2); + + RBeginMember c[2]; + assert(std::ranges::rbegin(c).base() == c + 2); + assert(std::ranges::crbegin(c).base() == c + 2); + + return true; +} + +struct RBeginMemberReturnsInt { + int rbegin() const; +}; +static_assert(!std::is_invocable_v); + +struct RBeginMemberReturnsVoidPtr { + const void *rbegin() const; +}; +static_assert(!std::is_invocable_v); + +struct PtrConvertibleRBeginMember { + struct iterator { operator int*() const; }; + iterator rbegin() const; +}; +static_assert(!std::is_invocable_v); + +struct NonConstRBeginMember { + int x; + constexpr int* rbegin() { 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 EnabledBorrowingRBeginMember { + constexpr int *rbegin() const { return globalBuff; } +}; +template<> +inline constexpr bool std::ranges::enable_borrowed_range = true; + +struct RBeginMemberFunction { + int x; + constexpr const int *rbegin() const { return &x; } + friend int* rbegin(RBeginMemberFunction const&); +}; + +struct EmptyPtrRBeginMember { + struct Empty {}; + Empty x; + constexpr const Empty* rbegin() const { return &x; } +}; + +constexpr bool testRBeginMember() { + RBeginMember a; + assert(std::ranges::rbegin(a) == &a.x); + assert(std::ranges::crbegin(a) == &a.x); + static_assert(!std::is_invocable_v); + static_assert(!std::is_invocable_v); + + NonConstRBeginMember b; + assert(std::ranges::rbegin(b) == &b.x); + static_assert(!std::is_invocable_v); + + EnabledBorrowingRBeginMember c; + assert(std::ranges::rbegin(c) == globalBuff); + assert(std::ranges::crbegin(c) == globalBuff); + assert(std::ranges::rbegin(std::move(c)) == globalBuff); + assert(std::ranges::crbegin(std::move(c)) == globalBuff); + + RBeginMemberFunction d; + assert(std::ranges::rbegin(d) == &d.x); + assert(std::ranges::crbegin(d) == &d.x); + + EmptyPtrRBeginMember e; + assert(std::ranges::rbegin(e) == &e.x); + assert(std::ranges::crbegin(e) == &e.x); + + return true; +} + + +struct RBeginFunction { + int x; + friend constexpr const int* rbegin(RBeginFunction 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 RBeginFunctionReturnsInt { + friend int rbegin(RBeginFunctionReturnsInt const&); +}; +static_assert(!std::is_invocable_v); + +struct RBeginFunctionReturnsVoidPtr { + friend void *rbegin(RBeginFunctionReturnsVoidPtr const&); +}; +static_assert(!std::is_invocable_v); + +struct RBeginFunctionReturnsEmpty { + struct Empty {}; + friend Empty rbegin(RBeginFunctionReturnsEmpty const&); +}; +static_assert(!std::is_invocable_v); + +struct RBeginFunctionReturnsPtrConvertible { + struct iterator { operator int*() const; }; + friend iterator rbegin(RBeginFunctionReturnsPtrConvertible const&); +}; +static_assert(!std::is_invocable_v); + +struct RBeginFunctionByValue { + friend constexpr int *rbegin(RBeginFunctionByValue) { return globalBuff + 1; } +}; +static_assert(!std::is_invocable_v); + +struct RBeginFunctionEnabledBorrowing { + friend constexpr int *rbegin(RBeginFunctionEnabledBorrowing) { return globalBuff + 2; } +}; +template<> +inline constexpr bool std::ranges::enable_borrowed_range = true; + +struct RBeginFunctionReturnsEmptyPtr { + struct Empty {}; + Empty x; + friend constexpr const Empty *rbegin(RBeginFunctionReturnsEmptyPtr const& bf) { return &bf.x; } +}; + +struct RBeginFunctionWithDataMember { + int x; + int rbegin; + friend constexpr const int *rbegin(RBeginFunctionWithDataMember const& bf) { return &bf.x; } +}; + +struct RBeginFunctionWithPrivateBeginMember { + int y; + friend constexpr const int *rbegin(RBeginFunctionWithPrivateBeginMember const& bf) { return &bf.y; } +private: + const int *rbegin() const; +}; + +constexpr bool testRBeginFunction() { + RBeginFunction a{}; + const RBeginFunction aa{}; + static_assert(!std::invocable); + assert(std::ranges::crbegin(a) == &a.x); + assert(std::ranges::rbegin(aa) == &aa.x); + assert(std::ranges::crbegin(aa) == &aa.x); + + RBeginFunctionByValue b{}; + const RBeginFunctionByValue bb{}; + assert(std::ranges::rbegin(b) == globalBuff + 1); + assert(std::ranges::crbegin(b) == globalBuff + 1); + assert(std::ranges::rbegin(bb) == globalBuff + 1); + assert(std::ranges::crbegin(bb) == globalBuff + 1); + + RBeginFunctionEnabledBorrowing c{}; + const RBeginFunctionEnabledBorrowing cc{}; + assert(std::ranges::rbegin(std::move(c)) == globalBuff + 2); + assert(std::ranges::crbegin(std::move(c)) == globalBuff + 2); + assert(std::ranges::rbegin(std::move(cc)) == globalBuff + 2); + assert(std::ranges::crbegin(std::move(cc)) == globalBuff + 2); + + RBeginFunctionReturnsEmptyPtr d{}; + const RBeginFunctionReturnsEmptyPtr dd{}; + static_assert(!std::invocable); + assert(std::ranges::crbegin(d) == &d.x); + assert(std::ranges::rbegin(dd) == &dd.x); + assert(std::ranges::crbegin(dd) == &dd.x); + + RBeginFunctionWithDataMember e{}; + const RBeginFunctionWithDataMember ee{}; + static_assert(!std::invocable); + assert(std::ranges::rbegin(ee) == &ee.x); + assert(std::ranges::crbegin(e) == &e.x); + assert(std::ranges::crbegin(ee) == &ee.x); + + RBeginFunctionWithPrivateBeginMember f{}; + const RBeginFunctionWithPrivateBeginMember ff{}; + static_assert(!std::invocable); + assert(std::ranges::crbegin(f) == &f.y); + assert(std::ranges::rbegin(ff) == &ff.y); + assert(std::ranges::crbegin(ff) == &ff.y); + + return true; +} + + +struct MemberBeginEnd { + int b, e; + char cb, ce; + constexpr bidirectional_iterator begin() { return bidirectional_iterator(&b); } + constexpr bidirectional_iterator end() { return bidirectional_iterator(&e); } + constexpr bidirectional_iterator begin() const { return bidirectional_iterator(&cb); } + constexpr bidirectional_iterator end() const { return bidirectional_iterator(&ce); } +}; +static_assert( std::is_invocable_v); +static_assert( std::is_invocable_v); +static_assert( std::is_invocable_v); + +struct FunctionBeginEnd { + int b, e; + char cb, ce; + friend constexpr bidirectional_iterator begin(FunctionBeginEnd& v) { + return bidirectional_iterator(&v.b); + } + friend constexpr bidirectional_iterator end(FunctionBeginEnd& v) { return bidirectional_iterator(&v.e); } + friend constexpr bidirectional_iterator begin(const FunctionBeginEnd& v) { + return bidirectional_iterator(&v.cb); + } + friend constexpr bidirectional_iterator end(const FunctionBeginEnd& v) { + return bidirectional_iterator(&v.ce); + } +}; +static_assert( std::is_invocable_v); +static_assert( std::is_invocable_v); +static_assert( std::is_invocable_v); + +struct MemberBeginFunctionEnd { + int b, e; + char cb, ce; + constexpr bidirectional_iterator begin() { return bidirectional_iterator(&b); } + friend constexpr bidirectional_iterator end(MemberBeginFunctionEnd& v) { + return bidirectional_iterator(&v.e); + } + constexpr bidirectional_iterator begin() const { return bidirectional_iterator(&cb); } + friend constexpr bidirectional_iterator end(const MemberBeginFunctionEnd& v) { + return bidirectional_iterator(&v.ce); + } +}; +static_assert( std::is_invocable_v); +static_assert( std::is_invocable_v); +static_assert( std::is_invocable_v); + +struct FunctionBeginMemberEnd { + int b, e; + char cb, ce; + friend constexpr bidirectional_iterator begin(FunctionBeginMemberEnd& v) { + return bidirectional_iterator(&v.b); + } + constexpr bidirectional_iterator end() { return bidirectional_iterator(&e); } + friend constexpr bidirectional_iterator begin(const FunctionBeginMemberEnd& v) { + return bidirectional_iterator(&v.cb); + } + constexpr bidirectional_iterator end() const { return bidirectional_iterator(&ce); } +}; +static_assert( std::is_invocable_v); +static_assert( std::is_invocable_v); +static_assert( std::is_invocable_v); + +struct MemberBeginEndDifferentTypes { + bidirectional_iterator begin(); + bidirectional_iterator end(); +}; +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); + +struct FunctionBeginEndDifferentTypes { + friend bidirectional_iterator begin(FunctionBeginEndDifferentTypes&); + friend bidirectional_iterator end(FunctionBeginEndDifferentTypes&); +}; +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); + +struct MemberBeginEndForwardIterators { + forward_iterator begin(); + forward_iterator end(); +}; +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); + +struct MemberEndOnly { + bidirectional_iterator end() const; +}; +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); + +// Make sure there is no clash between the following cases: +// - the case that handles classes defining member `rbegin` and `rend` functions; +// - the case that handles classes defining `begin` and `end` functions returning reversible iterators. +struct MemberBeginAndRBegin { + int* begin() const; + int* end() const; + int* rbegin() const; + int* rend() const; +}; +static_assert( std::is_invocable_v); +static_assert( std::is_invocable_v); +static_assert( std::same_as, int*>); +static_assert( std::same_as, int*>); + +constexpr bool testBeginEnd() { + MemberBeginEnd a{}; + const MemberBeginEnd aa{}; + assert(std::ranges::rbegin(a).base().base() == &a.e); + assert(std::ranges::crbegin(a).base().base() == &a.ce); + assert(std::ranges::rbegin(aa).base().base() == &aa.ce); + assert(std::ranges::crbegin(aa).base().base() == &aa.ce); + + FunctionBeginEnd b{}; + const FunctionBeginEnd bb{}; + assert(std::ranges::rbegin(b).base().base() == &b.e); + assert(std::ranges::crbegin(b).base().base() == &b.ce); + assert(std::ranges::rbegin(bb).base().base() == &bb.ce); + assert(std::ranges::crbegin(bb).base().base() == &bb.ce); + + MemberBeginFunctionEnd c{}; + const MemberBeginFunctionEnd cc{}; + assert(std::ranges::rbegin(c).base().base() == &c.e); + assert(std::ranges::crbegin(c).base().base() == &c.ce); + assert(std::ranges::rbegin(cc).base().base() == &cc.ce); + assert(std::ranges::crbegin(cc).base().base() == &cc.ce); + + FunctionBeginMemberEnd d{}; + const FunctionBeginMemberEnd dd{}; + assert(std::ranges::rbegin(d).base().base() == &d.e); + assert(std::ranges::crbegin(d).base().base() == &d.ce); + assert(std::ranges::rbegin(dd).base().base() == &dd.ce); + assert(std::ranges::crbegin(dd).base().base() == &dd.ce); + + return true; +} + + +// The `rbegin` overload that uses `std::make_reverse_iterator` is never `noexcept` because `make_reverse_iterator` is +// not `noexcept`. +static_assert(!noexcept(std::ranges::rbegin(std::declval()))); +static_assert(!noexcept(std::ranges::crbegin(std::declval()))); + +struct NoThrowMemberRBegin { + ThrowingIterator rbegin() const noexcept; // auto(t.rbegin()) doesn't throw +} ntmb; +static_assert(noexcept(std::ranges::rbegin(ntmb))); +static_assert(noexcept(std::ranges::crbegin(ntmb))); + +struct NoThrowADLRBegin { + friend ThrowingIterator rbegin(NoThrowADLRBegin&) noexcept; // auto(rbegin(t)) doesn't throw + friend ThrowingIterator rbegin(const NoThrowADLRBegin&) noexcept; +} ntab; +static_assert(noexcept(std::ranges::rbegin(ntab))); +static_assert(noexcept(std::ranges::crbegin(ntab))); + +struct NoThrowMemberRBeginReturnsRef { + ThrowingIterator& rbegin() const noexcept; // auto(t.rbegin()) may throw +} ntmbrr; +static_assert(!noexcept(std::ranges::rbegin(ntmbrr))); +static_assert(!noexcept(std::ranges::crbegin(ntmbrr))); + +struct RBeginReturnsArrayRef { + auto rbegin() const noexcept -> int(&)[10]; +} brar; +static_assert(noexcept(std::ranges::rbegin(brar))); +static_assert(noexcept(std::ranges::crbegin(brar))); + +struct NoThrowBeginEnd { + int* begin() const noexcept; + int* end() const noexcept; +} ntbe; +// The `make_reverse_iterator` case is never `noexcept`. +static_assert(!noexcept(std::ranges::rbegin(ntbe))); +static_assert(!noexcept(std::ranges::crbegin(ntbe))); + +// 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()); + + testRBeginMember(); + static_assert(testRBeginMember()); + + testRBeginFunction(); + static_assert(testRBeginFunction()); + + testBeginEnd(); + static_assert(testBeginEnd()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.access/rend.pass.cpp b/libcxx/test/std/ranges/range.access/rend.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.access/rend.pass.cpp @@ -0,0 +1,522 @@ +//===----------------------------------------------------------------------===// +// +// 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: libcpp-has-no-incomplete-ranges + +// std::ranges::rend +// std::ranges::crend + +#include + +#include +#include +#include "test_macros.h" +#include "test_iterators.h" + +using RangeREndT = decltype(std::ranges::rend); +using RangeCREndT = decltype(std::ranges::crend); + +static int globalBuff[8]; + +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert( std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert( std::is_invocable_v); + +struct Incomplete; +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); + +struct REndMember { + int x; + const int* rbegin() const; + constexpr const int* rend() const { return &x; } +}; + +// Ensure that we can't call with rvalues with borrowing disabled. +static_assert( std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert( std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert( std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert( std::is_invocable_v); +static_assert(!std::is_invocable_v); + +constexpr bool testReturnTypes() { + { + int *x[2]; + ASSERT_SAME_TYPE(decltype(std::ranges::rend(x)), std::reverse_iterator); + ASSERT_SAME_TYPE(decltype(std::ranges::crend(x)), std::reverse_iterator); + } + + { + int x[2][2]; + ASSERT_SAME_TYPE(decltype(std::ranges::rend(x)), std::reverse_iterator); + ASSERT_SAME_TYPE(decltype(std::ranges::crend(x)), std::reverse_iterator); + } + + { + struct Different { + char* rbegin(); + sentinel_wrapper& rend(); + short* rbegin() const; + sentinel_wrapper& rend() const; + } x; + ASSERT_SAME_TYPE(decltype(std::ranges::rend(x)), sentinel_wrapper); + ASSERT_SAME_TYPE(decltype(std::ranges::crend(x)), sentinel_wrapper); + } + + return true; +} + +constexpr bool testArray() { + int a[2]; + assert(std::ranges::rend(a).base() == a); + assert(std::ranges::crend(a).base() == a); + + int b[2][2]; + assert(std::ranges::rend(b).base() == b); + assert(std::ranges::crend(b).base() == b); + + REndMember c[2]; + assert(std::ranges::rend(c).base() == c); + assert(std::ranges::crend(c).base() == c); + + return true; +} + +struct REndMemberReturnsInt { + int rbegin() const; + int rend() const; +}; +static_assert(!std::is_invocable_v); + +struct REndMemberReturnsVoidPtr { + const void *rbegin() const; + const void *rend() const; +}; +static_assert(!std::is_invocable_v); + +struct PtrConvertible { + operator int*() const; +}; +struct PtrConvertibleREndMember { + PtrConvertible rbegin() const; + PtrConvertible rend() const; +}; +static_assert(!std::is_invocable_v); + +struct NoRBeginMember { + constexpr const int* rend(); +}; +static_assert(!std::is_invocable_v); + +struct NonConstREndMember { + int x; + constexpr int* rbegin() { return nullptr; } + constexpr int* rend() { return &x; } +}; +static_assert( std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); + +struct EnabledBorrowingREndMember { + constexpr int* rbegin() const { return nullptr; } + constexpr int* rend() const { return &globalBuff[0]; } +}; + +template <> +inline constexpr bool std::ranges::enable_borrowed_range = true; + +struct REndMemberFunction { + int x; + constexpr const int* rbegin() const { return nullptr; } + constexpr const int* rend() const { return &x; } + friend constexpr int* rend(REndMemberFunction const&); +}; + +struct Empty { }; +struct EmptyEndMember { + Empty rbegin() const; + Empty rend() const; +}; +static_assert(!std::is_invocable_v); + +struct EmptyPtrREndMember { + Empty x; + constexpr const Empty* rbegin() const { return nullptr; } + constexpr const Empty* rend() const { return &x; } +}; + +constexpr bool testREndMember() { + REndMember a; + assert(std::ranges::rend(a) == &a.x); + assert(std::ranges::crend(a) == &a.x); + + NonConstREndMember b; + assert(std::ranges::rend(b) == &b.x); + static_assert(!std::is_invocable_v); + + EnabledBorrowingREndMember c; + assert(std::ranges::rend(std::move(c)) == &globalBuff[0]); + assert(std::ranges::crend(std::move(c)) == &globalBuff[0]); + + REndMemberFunction d; + assert(std::ranges::rend(d) == &d.x); + assert(std::ranges::crend(d) == &d.x); + + EmptyPtrREndMember e; + assert(std::ranges::rend(e) == &e.x); + assert(std::ranges::crend(e) == &e.x); + + return true; +} + +struct REndFunction { + int x; + friend constexpr const int* rbegin(REndFunction const&) { return nullptr; } + friend constexpr const int* rend(REndFunction const& bf) { return &bf.x; } +}; + +static_assert( std::is_invocable_v); +static_assert(!std::is_invocable_v); + +static_assert( std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert( std::is_invocable_v); +static_assert( std::is_invocable_v); + +struct REndFunctionReturnsInt { + friend constexpr int rbegin(REndFunctionReturnsInt const&); + friend constexpr int rend(REndFunctionReturnsInt const&); +}; +static_assert(!std::is_invocable_v); + +struct REndFunctionReturnsVoidPtr { + friend constexpr void* rbegin(REndFunctionReturnsVoidPtr const&); + friend constexpr void* rend(REndFunctionReturnsVoidPtr const&); +}; +static_assert(!std::is_invocable_v); + +struct REndFunctionReturnsEmpty { + friend constexpr Empty rbegin(REndFunctionReturnsEmpty const&); + friend constexpr Empty rend(REndFunctionReturnsEmpty const&); +}; +static_assert(!std::is_invocable_v); + +struct REndFunctionReturnsPtrConvertible { + friend constexpr PtrConvertible rbegin(REndFunctionReturnsPtrConvertible const&); + friend constexpr PtrConvertible rend(REndFunctionReturnsPtrConvertible const&); +}; +static_assert(!std::is_invocable_v); + +struct NoRBeginFunction { + friend constexpr const int* rend(NoRBeginFunction const&); +}; +static_assert(!std::is_invocable_v); + +struct REndFunctionByValue { + friend constexpr int* rbegin(REndFunctionByValue) { return nullptr; } + friend constexpr int* rend(REndFunctionByValue) { return &globalBuff[1]; } +}; +static_assert(!std::is_invocable_v); + +struct REndFunctionEnabledBorrowing { + friend constexpr int* rbegin(REndFunctionEnabledBorrowing) { return nullptr; } + friend constexpr int* rend(REndFunctionEnabledBorrowing) { return &globalBuff[2]; } +}; +template<> +inline constexpr bool std::ranges::enable_borrowed_range = true; + +struct REndFunctionReturnsEmptyPtr { + Empty x; + friend constexpr const Empty* rbegin(REndFunctionReturnsEmptyPtr const&) { return nullptr; } + friend constexpr const Empty* rend(REndFunctionReturnsEmptyPtr const& bf) { return &bf.x; } +}; + +struct REndFunctionWithDataMember { + int x; + int rend; + friend constexpr const int* rbegin(REndFunctionWithDataMember const&) { return nullptr; } + friend constexpr const int* rend(REndFunctionWithDataMember const& bf) { return &bf.x; } +}; + +struct REndFunctionWithPrivateEndMember : private REndMember { + int y; + friend constexpr const int* rbegin(REndFunctionWithPrivateEndMember const&) { return nullptr; } + friend constexpr const int* rend(REndFunctionWithPrivateEndMember const& bf) { return &bf.y; } +}; + +struct RBeginMemberEndFunction { + int x; + constexpr const int* rbegin() const { return nullptr; } + friend constexpr const int* rend(RBeginMemberEndFunction const& bf) { return &bf.x; } +}; + +constexpr bool testREndFunction() { + const REndFunction a{}; + assert(std::ranges::rend(a) == &a.x); + assert(std::ranges::crend(a) == &a.x); + REndFunction aa{}; + static_assert(!std::is_invocable_v); + assert(std::ranges::crend(aa) == &aa.x); + + REndFunctionByValue b; + assert(std::ranges::rend(b) == &globalBuff[1]); + assert(std::ranges::crend(b) == &globalBuff[1]); + + REndFunctionEnabledBorrowing c; + assert(std::ranges::rend(std::move(c)) == &globalBuff[2]); + assert(std::ranges::crend(std::move(c)) == &globalBuff[2]); + + const REndFunctionReturnsEmptyPtr d{}; + assert(std::ranges::rend(d) == &d.x); + assert(std::ranges::crend(d) == &d.x); + REndFunctionReturnsEmptyPtr dd{}; + static_assert(!std::is_invocable_v); + assert(std::ranges::crend(dd) == &dd.x); + + const REndFunctionWithDataMember e{}; + assert(std::ranges::rend(e) == &e.x); + assert(std::ranges::crend(e) == &e.x); + REndFunctionWithDataMember ee{}; + static_assert(!std::is_invocable_v); + assert(std::ranges::crend(ee) == &ee.x); + + const REndFunctionWithPrivateEndMember f{}; + assert(std::ranges::rend(f) == &f.y); + assert(std::ranges::crend(f) == &f.y); + REndFunctionWithPrivateEndMember ff{}; + static_assert(!std::is_invocable_v); + assert(std::ranges::crend(ff) == &ff.y); + + const RBeginMemberEndFunction g{}; + assert(std::ranges::rend(g) == &g.x); + assert(std::ranges::crend(g) == &g.x); + RBeginMemberEndFunction gg{}; + static_assert(!std::is_invocable_v); + assert(std::ranges::crend(gg) == &gg.x); + + return true; +} + + +struct MemberBeginEnd { + int b, e; + char cb, ce; + constexpr bidirectional_iterator begin() { return bidirectional_iterator(&b); } + constexpr bidirectional_iterator end() { return bidirectional_iterator(&e); } + constexpr bidirectional_iterator begin() const { return bidirectional_iterator(&cb); } + constexpr bidirectional_iterator end() const { return bidirectional_iterator(&ce); } +}; +static_assert( std::is_invocable_v); +static_assert( std::is_invocable_v); +static_assert( std::is_invocable_v); + +struct FunctionBeginEnd { + int b, e; + char cb, ce; + friend constexpr bidirectional_iterator begin(FunctionBeginEnd& v) { + return bidirectional_iterator(&v.b); + } + friend constexpr bidirectional_iterator end(FunctionBeginEnd& v) { return bidirectional_iterator(&v.e); } + friend constexpr bidirectional_iterator begin(const FunctionBeginEnd& v) { + return bidirectional_iterator(&v.cb); + } + friend constexpr bidirectional_iterator end(const FunctionBeginEnd& v) { + return bidirectional_iterator(&v.ce); + } +}; +static_assert( std::is_invocable_v); +static_assert( std::is_invocable_v); +static_assert( std::is_invocable_v); + +struct MemberBeginFunctionEnd { + int b, e; + char cb, ce; + constexpr bidirectional_iterator begin() { return bidirectional_iterator(&b); } + friend constexpr bidirectional_iterator end(MemberBeginFunctionEnd& v) { + return bidirectional_iterator(&v.e); + } + constexpr bidirectional_iterator begin() const { return bidirectional_iterator(&cb); } + friend constexpr bidirectional_iterator end(const MemberBeginFunctionEnd& v) { + return bidirectional_iterator(&v.ce); + } +}; +static_assert( std::is_invocable_v); +static_assert( std::is_invocable_v); +static_assert( std::is_invocable_v); + +struct FunctionBeginMemberEnd { + int b, e; + char cb, ce; + friend constexpr bidirectional_iterator begin(FunctionBeginMemberEnd& v) { + return bidirectional_iterator(&v.b); + } + constexpr bidirectional_iterator end() { return bidirectional_iterator(&e); } + friend constexpr bidirectional_iterator begin(const FunctionBeginMemberEnd& v) { + return bidirectional_iterator(&v.cb); + } + constexpr bidirectional_iterator end() const { return bidirectional_iterator(&ce); } +}; +static_assert( std::is_invocable_v); +static_assert( std::is_invocable_v); +static_assert( std::is_invocable_v); + +struct MemberBeginEndDifferentTypes { + bidirectional_iterator begin(); + bidirectional_iterator end(); +}; +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); + +struct FunctionBeginEndDifferentTypes { + friend bidirectional_iterator begin(FunctionBeginEndDifferentTypes&); + friend bidirectional_iterator end(FunctionBeginEndDifferentTypes&); +}; +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); + +struct MemberBeginEndForwardIterators { + forward_iterator begin(); + forward_iterator end(); +}; +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); + +struct MemberBeginOnly { + bidirectional_iterator begin() const; +}; +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); + +// Make sure there is no clash between the following cases: +// - the case that handles classes defining member `rbegin` and `rend` functions; +// - the case that handles classes defining `begin` and `end` functions returning reversible iterators. +struct MemberBeginAndRBegin { + int* begin() const; + int* end() const; + int* rbegin() const; + int* rend() const; +}; +static_assert( std::is_invocable_v); +static_assert( std::is_invocable_v); +static_assert( std::same_as, int*>); +static_assert( std::same_as, int*>); + +constexpr bool testBeginEnd() { + MemberBeginEnd a{}; + const MemberBeginEnd aa{}; + assert(std::ranges::rend(a).base().base() == &a.b); + assert(std::ranges::crend(a).base().base() == &a.cb); + assert(std::ranges::rend(aa).base().base() == &aa.cb); + assert(std::ranges::crend(aa).base().base() == &aa.cb); + + FunctionBeginEnd b{}; + const FunctionBeginEnd bb{}; + assert(std::ranges::rend(b).base().base() == &b.b); + assert(std::ranges::crend(b).base().base() == &b.cb); + assert(std::ranges::rend(bb).base().base() == &bb.cb); + assert(std::ranges::crend(bb).base().base() == &bb.cb); + + MemberBeginFunctionEnd c{}; + const MemberBeginFunctionEnd cc{}; + assert(std::ranges::rend(c).base().base() == &c.b); + assert(std::ranges::crend(c).base().base() == &c.cb); + assert(std::ranges::rend(cc).base().base() == &cc.cb); + assert(std::ranges::crend(cc).base().base() == &cc.cb); + + FunctionBeginMemberEnd d{}; + const FunctionBeginMemberEnd dd{}; + assert(std::ranges::rend(d).base().base() == &d.b); + assert(std::ranges::crend(d).base().base() == &d.cb); + assert(std::ranges::rend(dd).base().base() == &dd.cb); + assert(std::ranges::crend(dd).base().base() == &dd.cb); + + return true; +} + + +// The `rend` overload that uses `std::make_reverse_iterator` is never `noexcept` because `make_reverse_iterator` is not +// `noexcept`. +static_assert(!noexcept(std::ranges::rend(std::declval()))); +static_assert(!noexcept(std::ranges::crend(std::declval()))); + +struct NoThrowMemberREnd { + ThrowingIterator rbegin() const; + ThrowingIterator rend() const noexcept; // auto(t.rend()) doesn't throw +} ntmre; +static_assert(noexcept(std::ranges::rend(ntmre))); +static_assert(noexcept(std::ranges::crend(ntmre))); + +struct NoThrowADLREnd { + ThrowingIterator rbegin() const; + friend ThrowingIterator rend(NoThrowADLREnd&) noexcept; // auto(rend(t)) doesn't throw + friend ThrowingIterator rend(const NoThrowADLREnd&) noexcept; +} ntare; +static_assert(noexcept(std::ranges::rend(ntare))); +static_assert(noexcept(std::ranges::crend(ntare))); + +struct NoThrowMemberREndReturnsRef { + ThrowingIterator rbegin() const; + ThrowingIterator& rend() const noexcept; // auto(t.rend()) may throw +} ntmrerr; +static_assert(!noexcept(std::ranges::rend(ntmrerr))); +static_assert(!noexcept(std::ranges::crend(ntmrerr))); + +struct REndReturnsArrayRef { + auto rbegin() const noexcept -> int(&)[10]; + auto rend() const noexcept -> int(&)[10]; +} rerar; +static_assert(noexcept(std::ranges::rend(rerar))); +static_assert(noexcept(std::ranges::crend(rerar))); + +struct NoThrowBeginEnd { + int* begin() const noexcept; + int* end() const noexcept; +} ntbe; +// The `make_reverse_iterator` case is never `noexcept`. +static_assert(!noexcept(std::ranges::rend(ntbe))); +static_assert(!noexcept(std::ranges::crend(ntbe))); + +// Test ADL-proofing. +struct Incomplete; +template struct Holder { T t; }; +static_assert(!std::is_invocable_v*>); +static_assert(!std::is_invocable_v*&>); +static_assert(!std::is_invocable_v*>); +static_assert(!std::is_invocable_v*&>); + +int main(int, char**) { + static_assert(testReturnTypes()); + + testArray(); + static_assert(testArray()); + + testREndMember(); + static_assert(testREndMember()); + + testREndFunction(); + static_assert(testREndFunction()); + + testBeginEnd(); + static_assert(testBeginEnd()); + + return 0; +}