[libcxx-commits] [libcxx] 55bd22f - [libc++][ranges] Implement rbegin, rend, crbegin and crend.
Konstantin Varlamov via libcxx-commits
libcxx-commits at lists.llvm.org
Mon Feb 14 03:30:14 PST 2022
Author: Konstantin Varlamov
Date: 2022-02-14T03:29:59-08:00
New Revision: 55bd22f853d8acbfabdfd1086fa4f4d97db71f63
URL: https://github.com/llvm/llvm-project/commit/55bd22f853d8acbfabdfd1086fa4f4d97db71f63
DIFF: https://github.com/llvm/llvm-project/commit/55bd22f853d8acbfabdfd1086fa4f4d97db71f63.diff
LOG: [libc++][ranges] Implement rbegin, rend, crbegin and crend.
Differential Revision: https://reviews.llvm.org/D119057
Added:
libcxx/include/__ranges/rbegin.h
libcxx/include/__ranges/rend.h
libcxx/test/libcxx/diagnostics/detail.headers/ranges/rbegin.module.verify.cpp
libcxx/test/libcxx/diagnostics/detail.headers/ranges/rend.module.verify.cpp
libcxx/test/std/ranges/range.access/rbegin.pass.cpp
libcxx/test/std/ranges/range.access/rend.pass.cpp
Modified:
libcxx/docs/Status/RangesPaper.csv
libcxx/include/CMakeLists.txt
libcxx/include/__concepts/class_or_enum.h
libcxx/include/__ranges/access.h
libcxx/include/module.modulemap
libcxx/include/ranges
Removed:
################################################################################
diff --git a/libcxx/docs/Status/RangesPaper.csv b/libcxx/docs/Status/RangesPaper.csv
index bbb63d42b1333..0aa3504ca083e 100644
--- a/libcxx/docs/Status/RangesPaper.csv
+++ b/libcxx/docs/Status/RangesPaper.csv
@@ -100,15 +100,15 @@ Section,Description,Dependencies,Assignee,Complete
| `ranges::end <https://llvm.org/D100255>`_
| `range::cbegin <https://llvm.org/D100255>`_
| `ranges::cend <https://llvm.org/D100255>`_
-| ranges::rbegin
-| ranges::rend
-| ranges::crbegin
-| ranges::crend
+| `ranges::rbegin <https://llvm.org/D119057>`_
+| `ranges::rend <https://llvm.org/D119057>`_
+| `ranges::crbegin <https://llvm.org/D119057>`_
+| `ranges::crend <https://llvm.org/D119057>`_
| `ranges::size <https://llvm.org/D101079>`_
| `ranges::ssize <https://llvm.org/D101189>`_
| `ranges::empty <https://llvm.org/D101193>`_
| `ranges::data <https://llvm.org/D101476>`_
-| `ranges::cdata <https://llvm.org/D117044>`_",[iterator.concepts],Christopher Di Bella and Zoe Carver,In progress
+| `ranges::cdata <https://llvm.org/D117044>`_",[iterator.concepts],Various,✅
`[range.range] <https://wg21.link/range.range>`_,"| `ranges::range <https://llvm.org/D100269>`_
| `ranges::borrowed_range <https://llvm.org/D102426>`_
| `ranges::enable_borrowed_range <https://llvm.org/D90999>`_
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 877befc6b96b3..eff6521b74b42 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -346,7 +346,9 @@ set(files
__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
index f53d8dd074c9c..729e444b39ada 100644
--- a/libcxx/include/__concepts/class_or_enum.h
+++ b/libcxx/include/__concepts/class_or_enum.h
@@ -26,6 +26,7 @@ template<class _Tp>
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<class _Tp>
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
index 5b623c1e4a2a8..2ebdab4eb8cd9 100644
--- a/libcxx/include/__ranges/access.h
+++ b/libcxx/include/__ranges/access.h
@@ -15,7 +15,6 @@
#include <__iterator/readable_traits.h>
#include <__ranges/enable_borrowed_range.h>
#include <__utility/auto_cast.h>
-#include <concepts>
#include <type_traits>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
diff --git a/libcxx/include/__ranges/rbegin.h b/libcxx/include/__ranges/rbegin.h
new file mode 100644
index 0000000000000..cc4c0582cc246
--- /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 <type_traits>
+
+#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 <class _Tp>
+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 <class _Tp>
+concept __unqualified_rbegin =
+ !__member_rbegin<_Tp> &&
+ __can_borrow<_Tp> &&
+ __class_or_enum<remove_cvref_t<_Tp>> &&
+ requires(_Tp&& __t) {
+ { _LIBCPP_AUTO_CAST(rbegin(__t)) } -> input_or_output_iterator;
+ };
+
+template <class _Tp>
+concept __can_reverse =
+ __can_borrow<_Tp> &&
+ !__member_rbegin<_Tp> &&
+ !__unqualified_rbegin<_Tp> &&
+ requires(_Tp&& __t) {
+ { ranges::begin(__t) } -> same_as<decltype(ranges::end(__t))>;
+ { ranges::begin(__t) } -> bidirectional_iterator;
+ };
+
+struct __fn {
+ template <class _Tp>
+ 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 <class _Tp>
+ 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 <class _Tp>
+ requires __can_reverse<_Tp>
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const
+ noexcept(noexcept(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 <class _Tp>
+ requires is_lvalue_reference_v<_Tp&&>
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI
+ constexpr auto operator()(_Tp&& __t) const
+ noexcept(noexcept(ranges::rbegin(static_cast<const remove_reference_t<_Tp>&>(__t))))
+ -> decltype( ranges::rbegin(static_cast<const remove_reference_t<_Tp>&>(__t)))
+ { return ranges::rbegin(static_cast<const remove_reference_t<_Tp>&>(__t)); }
+
+ template <class _Tp>
+ requires is_rvalue_reference_v<_Tp&&>
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI
+ constexpr auto operator()(_Tp&& __t) const
+ noexcept(noexcept(ranges::rbegin(static_cast<const _Tp&&>(__t))))
+ -> decltype( ranges::rbegin(static_cast<const _Tp&&>(__t)))
+ { return ranges::rbegin(static_cast<const _Tp&&>(__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
index 0000000000000..cd7826021d446
--- /dev/null
+++ b/libcxx/include/__ranges/rend.h
@@ -0,0 +1,134 @@
+// -*- 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 <type_traits>
+
+#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 <class _Tp>
+concept __member_rend =
+ __can_borrow<_Tp> &&
+ __workaround_52970<_Tp> &&
+ requires(_Tp&& __t) {
+ ranges::rbegin(__t);
+ { _LIBCPP_AUTO_CAST(__t.rend()) } -> sentinel_for<decltype(ranges::rbegin(__t))>;
+ };
+
+void rend(auto&) = delete;
+void rend(const auto&) = delete;
+
+template <class _Tp>
+concept __unqualified_rend =
+ !__member_rend<_Tp> &&
+ __can_borrow<_Tp> &&
+ __class_or_enum<remove_cvref_t<_Tp>> &&
+ requires(_Tp&& __t) {
+ ranges::rbegin(__t);
+ { _LIBCPP_AUTO_CAST(rend(__t)) } -> sentinel_for<decltype(ranges::rbegin(__t))>;
+ };
+
+template <class _Tp>
+concept __can_reverse =
+ __can_borrow<_Tp> &&
+ !__member_rend<_Tp> &&
+ !__unqualified_rend<_Tp> &&
+ requires(_Tp&& __t) {
+ { ranges::begin(__t) } -> same_as<decltype(ranges::end(__t))>;
+ { ranges::begin(__t) } -> bidirectional_iterator;
+ };
+
+class __fn {
+public:
+ template <class _Tp>
+ 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 <class _Tp>
+ 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 <class _Tp>
+ requires __can_reverse<_Tp>
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const
+ noexcept(noexcept(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 <class _Tp>
+ requires is_lvalue_reference_v<_Tp&&>
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI
+ constexpr auto operator()(_Tp&& __t) const
+ noexcept(noexcept(ranges::rend(static_cast<const remove_reference_t<_Tp>&>(__t))))
+ -> decltype( ranges::rend(static_cast<const remove_reference_t<_Tp>&>(__t)))
+ { return ranges::rend(static_cast<const remove_reference_t<_Tp>&>(__t)); }
+
+ template <class _Tp>
+ requires is_rvalue_reference_v<_Tp&&>
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI
+ constexpr auto operator()(_Tp&& __t) const
+ noexcept(noexcept(ranges::rend(static_cast<const _Tp&&>(__t))))
+ -> decltype( ranges::rend(static_cast<const _Tp&&>(__t)))
+ { return ranges::rend(static_cast<const _Tp&&>(__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
index 572705330fe0d..0ef457afe33ad 100644
--- a/libcxx/include/module.modulemap
+++ b/libcxx/include/module.modulemap
@@ -815,7 +815,9 @@ module std [system] {
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
index e8142084fe975..2bc9121cd68b4 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -250,7 +250,9 @@ namespace std {
#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
index 0000000000000..c91765bba5657
--- /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
index 0000000000000..460c5a9594a91
--- /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/rbegin.pass.cpp b/libcxx/test/std/ranges/range.access/rbegin.pass.cpp
new file mode 100644
index 0000000000000..47c65f4e1d7f7
--- /dev/null
+++ b/libcxx/test/std/ranges/range.access/rbegin.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::rbegin
+// std::ranges::crbegin
+
+#include <ranges>
+
+#include <cassert>
+#include <utility>
+#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<RangeRBeginT, int (&&)[10]>);
+static_assert( std::is_invocable_v<RangeRBeginT, int (&)[10]>);
+static_assert(!std::is_invocable_v<RangeRBeginT, int (&&)[]>);
+static_assert(!std::is_invocable_v<RangeRBeginT, int (&)[]>);
+static_assert(!std::is_invocable_v<RangeCRBeginT, int (&&)[10]>);
+static_assert( std::is_invocable_v<RangeCRBeginT, int (&)[10]>);
+static_assert(!std::is_invocable_v<RangeCRBeginT, int (&&)[]>);
+static_assert(!std::is_invocable_v<RangeCRBeginT, int (&)[]>);
+
+struct Incomplete;
+
+static_assert(!std::is_invocable_v<RangeRBeginT, Incomplete(&&)[]>);
+static_assert(!std::is_invocable_v<RangeRBeginT, const Incomplete(&&)[]>);
+static_assert(!std::is_invocable_v<RangeCRBeginT, Incomplete(&&)[]>);
+static_assert(!std::is_invocable_v<RangeCRBeginT, const Incomplete(&&)[]>);
+
+static_assert(!std::is_invocable_v<RangeRBeginT, Incomplete(&&)[10]>);
+static_assert(!std::is_invocable_v<RangeRBeginT, const Incomplete(&&)[10]>);
+static_assert(!std::is_invocable_v<RangeCRBeginT, Incomplete(&&)[10]>);
+static_assert(!std::is_invocable_v<RangeCRBeginT, const Incomplete(&&)[10]>);
+
+// This case is IFNDR; we handle it SFINAE-friendly.
+LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeRBeginT, Incomplete(&)[]>);
+LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeRBeginT, const Incomplete(&)[]>);
+LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeCRBeginT, Incomplete(&)[]>);
+LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeCRBeginT, const Incomplete(&)[]>);
+
+// This case is IFNDR; we handle it SFINAE-friendly.
+LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeRBeginT, Incomplete(&)[10]>);
+LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeRBeginT, const Incomplete(&)[10]>);
+LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeCRBeginT, Incomplete(&)[10]>);
+LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeCRBeginT, const Incomplete(&)[10]>);
+
+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<RangeRBeginT, RBeginMember &>);
+static_assert(!std::is_invocable_v<RangeRBeginT, RBeginMember &&>);
+static_assert( std::is_invocable_v<RangeRBeginT, RBeginMember const&>);
+static_assert(!std::is_invocable_v<RangeRBeginT, RBeginMember const&&>);
+static_assert( std::is_invocable_v<RangeCRBeginT, RBeginMember &>);
+static_assert(!std::is_invocable_v<RangeCRBeginT, RBeginMember &&>);
+static_assert( std::is_invocable_v<RangeCRBeginT, RBeginMember const&>);
+static_assert(!std::is_invocable_v<RangeCRBeginT, RBeginMember const&&>);
+
+constexpr bool testReturnTypes() {
+ {
+ int *x[2];
+ ASSERT_SAME_TYPE(decltype(std::ranges::rbegin(x)), std::reverse_iterator<int**>);
+ ASSERT_SAME_TYPE(decltype(std::ranges::crbegin(x)), std::reverse_iterator<int* const*>);
+ }
+ {
+ int x[2][2];
+ ASSERT_SAME_TYPE(decltype(std::ranges::rbegin(x)), std::reverse_iterator<int(*)[2]>);
+ ASSERT_SAME_TYPE(decltype(std::ranges::crbegin(x)), std::reverse_iterator<const int(*)[2]>);
+ }
+ {
+ 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<RangeRBeginT, RBeginMemberReturnsInt const&>);
+
+struct RBeginMemberReturnsVoidPtr {
+ const void *rbegin() const;
+};
+static_assert(!std::is_invocable_v<RangeRBeginT, RBeginMemberReturnsVoidPtr const&>);
+
+struct PtrConvertibleRBeginMember {
+ struct iterator { operator int*() const; };
+ iterator rbegin() const;
+};
+static_assert(!std::is_invocable_v<RangeRBeginT, PtrConvertibleRBeginMember const&>);
+
+struct NonConstRBeginMember {
+ int x;
+ constexpr int* rbegin() { return &x; }
+};
+static_assert( std::is_invocable_v<RangeRBeginT, NonConstRBeginMember &>);
+static_assert(!std::is_invocable_v<RangeRBeginT, NonConstRBeginMember const&>);
+static_assert(!std::is_invocable_v<RangeCRBeginT, NonConstRBeginMember &>);
+static_assert(!std::is_invocable_v<RangeCRBeginT, NonConstRBeginMember const&>);
+
+struct EnabledBorrowingRBeginMember {
+ constexpr int *rbegin() const { return globalBuff; }
+};
+template<>
+inline constexpr bool std::ranges::enable_borrowed_range<EnabledBorrowingRBeginMember> = 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<RangeRBeginT, RBeginMember&&>);
+ static_assert(!std::is_invocable_v<RangeCRBeginT, RBeginMember&&>);
+
+ NonConstRBeginMember b;
+ assert(std::ranges::rbegin(b) == &b.x);
+ static_assert(!std::is_invocable_v<RangeCRBeginT, NonConstRBeginMember&>);
+
+ 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<RangeRBeginT, RBeginFunction const&>);
+static_assert(!std::is_invocable_v<RangeRBeginT, RBeginFunction &&>);
+static_assert(!std::is_invocable_v<RangeRBeginT, RBeginFunction &>);
+static_assert( std::is_invocable_v<RangeCRBeginT, RBeginFunction const&>);
+static_assert( std::is_invocable_v<RangeCRBeginT, RBeginFunction &>);
+
+struct RBeginFunctionReturnsInt {
+ friend int rbegin(RBeginFunctionReturnsInt const&);
+};
+static_assert(!std::is_invocable_v<RangeRBeginT, RBeginFunctionReturnsInt const&>);
+
+struct RBeginFunctionReturnsVoidPtr {
+ friend void *rbegin(RBeginFunctionReturnsVoidPtr const&);
+};
+static_assert(!std::is_invocable_v<RangeRBeginT, RBeginFunctionReturnsVoidPtr const&>);
+
+struct RBeginFunctionReturnsEmpty {
+ struct Empty {};
+ friend Empty rbegin(RBeginFunctionReturnsEmpty const&);
+};
+static_assert(!std::is_invocable_v<RangeRBeginT, RBeginFunctionReturnsEmpty const&>);
+
+struct RBeginFunctionReturnsPtrConvertible {
+ struct iterator { operator int*() const; };
+ friend iterator rbegin(RBeginFunctionReturnsPtrConvertible const&);
+};
+static_assert(!std::is_invocable_v<RangeRBeginT, RBeginFunctionReturnsPtrConvertible const&>);
+
+struct RBeginFunctionByValue {
+ friend constexpr int *rbegin(RBeginFunctionByValue) { return globalBuff + 1; }
+};
+static_assert(!std::is_invocable_v<RangeCRBeginT, RBeginFunctionByValue>);
+
+struct RBeginFunctionEnabledBorrowing {
+ friend constexpr int *rbegin(RBeginFunctionEnabledBorrowing) { return globalBuff + 2; }
+};
+template<>
+inline constexpr bool std::ranges::enable_borrowed_range<RBeginFunctionEnabledBorrowing> = 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<RangeRBeginT, decltype((a))>);
+ 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<RangeRBeginT, decltype((d))>);
+ 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<RangeRBeginT, decltype((e))>);
+ 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<RangeRBeginT, decltype((f))>);
+ 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<int*> begin() { return bidirectional_iterator<int*>(&b); }
+ constexpr bidirectional_iterator<int*> end() { return bidirectional_iterator<int*>(&e); }
+ constexpr bidirectional_iterator<const char*> begin() const { return bidirectional_iterator<const char*>(&cb); }
+ constexpr bidirectional_iterator<const char*> end() const { return bidirectional_iterator<const char*>(&ce); }
+};
+static_assert( std::is_invocable_v<RangeRBeginT, MemberBeginEnd&>);
+static_assert( std::is_invocable_v<RangeRBeginT, MemberBeginEnd const&>);
+static_assert( std::is_invocable_v<RangeCRBeginT, MemberBeginEnd const&>);
+
+struct FunctionBeginEnd {
+ int b, e;
+ char cb, ce;
+ friend constexpr bidirectional_iterator<int*> begin(FunctionBeginEnd& v) {
+ return bidirectional_iterator<int*>(&v.b);
+ }
+ friend constexpr bidirectional_iterator<int*> end(FunctionBeginEnd& v) { return bidirectional_iterator<int*>(&v.e); }
+ friend constexpr bidirectional_iterator<const char*> begin(const FunctionBeginEnd& v) {
+ return bidirectional_iterator<const char*>(&v.cb);
+ }
+ friend constexpr bidirectional_iterator<const char*> end(const FunctionBeginEnd& v) {
+ return bidirectional_iterator<const char*>(&v.ce);
+ }
+};
+static_assert( std::is_invocable_v<RangeRBeginT, FunctionBeginEnd&>);
+static_assert( std::is_invocable_v<RangeRBeginT, FunctionBeginEnd const&>);
+static_assert( std::is_invocable_v<RangeCRBeginT, FunctionBeginEnd const&>);
+
+struct MemberBeginFunctionEnd {
+ int b, e;
+ char cb, ce;
+ constexpr bidirectional_iterator<int*> begin() { return bidirectional_iterator<int*>(&b); }
+ friend constexpr bidirectional_iterator<int*> end(MemberBeginFunctionEnd& v) {
+ return bidirectional_iterator<int*>(&v.e);
+ }
+ constexpr bidirectional_iterator<const char*> begin() const { return bidirectional_iterator<const char*>(&cb); }
+ friend constexpr bidirectional_iterator<const char*> end(const MemberBeginFunctionEnd& v) {
+ return bidirectional_iterator<const char*>(&v.ce);
+ }
+};
+static_assert( std::is_invocable_v<RangeRBeginT, MemberBeginFunctionEnd&>);
+static_assert( std::is_invocable_v<RangeRBeginT, MemberBeginFunctionEnd const&>);
+static_assert( std::is_invocable_v<RangeCRBeginT, MemberBeginFunctionEnd const&>);
+
+struct FunctionBeginMemberEnd {
+ int b, e;
+ char cb, ce;
+ friend constexpr bidirectional_iterator<int*> begin(FunctionBeginMemberEnd& v) {
+ return bidirectional_iterator<int*>(&v.b);
+ }
+ constexpr bidirectional_iterator<int*> end() { return bidirectional_iterator<int*>(&e); }
+ friend constexpr bidirectional_iterator<const char*> begin(const FunctionBeginMemberEnd& v) {
+ return bidirectional_iterator<const char*>(&v.cb);
+ }
+ constexpr bidirectional_iterator<const char*> end() const { return bidirectional_iterator<const char*>(&ce); }
+};
+static_assert( std::is_invocable_v<RangeRBeginT, FunctionBeginMemberEnd&>);
+static_assert( std::is_invocable_v<RangeRBeginT, FunctionBeginMemberEnd const&>);
+static_assert( std::is_invocable_v<RangeCRBeginT, FunctionBeginMemberEnd const&>);
+
+struct MemberBeginEndDifferentTypes {
+ bidirectional_iterator<int*> begin();
+ bidirectional_iterator<const int*> end();
+};
+static_assert(!std::is_invocable_v<RangeRBeginT, MemberBeginEndDifferentTypes&>);
+static_assert(!std::is_invocable_v<RangeCRBeginT, MemberBeginEndDifferentTypes&>);
+
+struct FunctionBeginEndDifferentTypes {
+ friend bidirectional_iterator<int*> begin(FunctionBeginEndDifferentTypes&);
+ friend bidirectional_iterator<const int*> end(FunctionBeginEndDifferentTypes&);
+};
+static_assert(!std::is_invocable_v<RangeRBeginT, FunctionBeginEndDifferentTypes&>);
+static_assert(!std::is_invocable_v<RangeCRBeginT, FunctionBeginEndDifferentTypes&>);
+
+struct MemberBeginEndForwardIterators {
+ forward_iterator<int*> begin();
+ forward_iterator<int*> end();
+};
+static_assert(!std::is_invocable_v<RangeRBeginT, MemberBeginEndForwardIterators&>);
+static_assert(!std::is_invocable_v<RangeCRBeginT, MemberBeginEndForwardIterators&>);
+
+struct FunctionBeginEndForwardIterators {
+ friend forward_iterator<int*> begin(FunctionBeginEndForwardIterators&);
+ friend forward_iterator<int*> end(FunctionBeginEndForwardIterators&);
+};
+static_assert(!std::is_invocable_v<RangeRBeginT, FunctionBeginEndForwardIterators&>);
+static_assert(!std::is_invocable_v<RangeCRBeginT, FunctionBeginEndForwardIterators&>);
+
+struct MemberBeginOnly {
+ bidirectional_iterator<int*> begin() const;
+};
+static_assert(!std::is_invocable_v<RangeRBeginT, MemberBeginOnly&>);
+static_assert(!std::is_invocable_v<RangeCRBeginT, MemberBeginOnly&>);
+
+struct FunctionBeginOnly {
+ friend bidirectional_iterator<int*> begin(FunctionBeginOnly&);
+};
+static_assert(!std::is_invocable_v<RangeRBeginT, FunctionBeginOnly&>);
+static_assert(!std::is_invocable_v<RangeCRBeginT, FunctionBeginOnly&>);
+
+struct MemberEndOnly {
+ bidirectional_iterator<int*> end() const;
+};
+static_assert(!std::is_invocable_v<RangeRBeginT, MemberEndOnly&>);
+static_assert(!std::is_invocable_v<RangeCRBeginT, MemberEndOnly&>);
+
+struct FunctionEndOnly {
+ friend bidirectional_iterator<int*> end(FunctionEndOnly&);
+};
+static_assert(!std::is_invocable_v<RangeRBeginT, FunctionEndOnly&>);
+static_assert(!std::is_invocable_v<RangeCRBeginT, FunctionEndOnly&>);
+
+// 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<RangeRBeginT, MemberBeginAndRBegin&>);
+static_assert( std::is_invocable_v<RangeCRBeginT, MemberBeginAndRBegin&>);
+static_assert( std::same_as<std::invoke_result_t<RangeRBeginT, MemberBeginAndRBegin&>, int*>);
+static_assert( std::same_as<std::invoke_result_t<RangeCRBeginT, MemberBeginAndRBegin&>, 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;
+}
+
+
+ASSERT_NOEXCEPT(std::ranges::rbegin(std::declval<int (&)[10]>()));
+ASSERT_NOEXCEPT(std::ranges::crbegin(std::declval<int (&)[10]>()));
+
+struct NoThrowMemberRBegin {
+ ThrowingIterator<int> 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<int> rbegin(NoThrowADLRBegin&) noexcept; // auto(rbegin(t)) doesn't throw
+ friend ThrowingIterator<int> rbegin(const NoThrowADLRBegin&) noexcept;
+} ntab;
+static_assert(noexcept(std::ranges::rbegin(ntab)));
+static_assert(noexcept(std::ranges::crbegin(ntab)));
+
+struct NoThrowMemberRBeginReturnsRef {
+ ThrowingIterator<int>& 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 NoThrowBeginThrowingEnd {
+ int* begin() const noexcept;
+ int* end() const;
+} ntbte;
+static_assert(!noexcept(std::ranges::rbegin(ntbte)));
+static_assert(!noexcept(std::ranges::crbegin(ntbte)));
+
+struct NoThrowEndThrowingBegin {
+ int* begin() const;
+ int* end() const noexcept;
+} ntetb;
+static_assert(noexcept(std::ranges::rbegin(ntetb)));
+static_assert(noexcept(std::ranges::crbegin(ntetb)));
+
+// Test ADL-proofing.
+struct Incomplete;
+template<class T> struct Holder { T t; };
+static_assert(!std::is_invocable_v<RangeRBeginT, Holder<Incomplete>*>);
+static_assert(!std::is_invocable_v<RangeRBeginT, Holder<Incomplete>*&>);
+static_assert(!std::is_invocable_v<RangeCRBeginT, Holder<Incomplete>*>);
+static_assert(!std::is_invocable_v<RangeCRBeginT, Holder<Incomplete>*&>);
+
+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
index 0000000000000..b15cec155b6ff
--- /dev/null
+++ b/libcxx/test/std/ranges/range.access/rend.pass.cpp
@@ -0,0 +1,551 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 <ranges>
+
+#include <cassert>
+#include <utility>
+#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<RangeREndT, int (&&)[]>);
+static_assert(!std::is_invocable_v<RangeREndT, int (&)[]>);
+static_assert(!std::is_invocable_v<RangeREndT, int (&&)[10]>);
+static_assert( std::is_invocable_v<RangeREndT, int (&)[10]>);
+static_assert(!std::is_invocable_v<RangeCREndT, int (&&)[]>);
+static_assert(!std::is_invocable_v<RangeCREndT, int (&)[]>);
+static_assert(!std::is_invocable_v<RangeCREndT, int (&&)[10]>);
+static_assert( std::is_invocable_v<RangeCREndT, int (&)[10]>);
+
+struct Incomplete;
+static_assert(!std::is_invocable_v<RangeREndT, Incomplete(&&)[]>);
+static_assert(!std::is_invocable_v<RangeREndT, Incomplete(&&)[42]>);
+static_assert(!std::is_invocable_v<RangeCREndT, Incomplete(&&)[]>);
+static_assert(!std::is_invocable_v<RangeCREndT, Incomplete(&&)[42]>);
+
+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<RangeREndT, REndMember&>);
+static_assert(!std::is_invocable_v<RangeREndT, REndMember &&>);
+static_assert( std::is_invocable_v<RangeREndT, REndMember const&>);
+static_assert(!std::is_invocable_v<RangeREndT, REndMember const&&>);
+static_assert( std::is_invocable_v<RangeCREndT, REndMember &>);
+static_assert(!std::is_invocable_v<RangeCREndT, REndMember &&>);
+static_assert( std::is_invocable_v<RangeCREndT, REndMember const&>);
+static_assert(!std::is_invocable_v<RangeCREndT, REndMember const&&>);
+
+constexpr bool testReturnTypes() {
+ {
+ int *x[2];
+ ASSERT_SAME_TYPE(decltype(std::ranges::rend(x)), std::reverse_iterator<int**>);
+ ASSERT_SAME_TYPE(decltype(std::ranges::crend(x)), std::reverse_iterator<int* const*>);
+ }
+
+ {
+ int x[2][2];
+ ASSERT_SAME_TYPE(decltype(std::ranges::rend(x)), std::reverse_iterator<int(*)[2]>);
+ ASSERT_SAME_TYPE(decltype(std::ranges::crend(x)), std::reverse_iterator<const int(*)[2]>);
+ }
+
+ {
+ struct Different {
+ char* rbegin();
+ sentinel_wrapper<char*>& rend();
+ short* rbegin() const;
+ sentinel_wrapper<short*>& rend() const;
+ } x;
+ ASSERT_SAME_TYPE(decltype(std::ranges::rend(x)), sentinel_wrapper<char*>);
+ ASSERT_SAME_TYPE(decltype(std::ranges::crend(x)), sentinel_wrapper<short*>);
+ }
+
+ 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<RangeREndT, REndMemberReturnsInt const&>);
+
+struct REndMemberReturnsVoidPtr {
+ const void *rbegin() const;
+ const void *rend() const;
+};
+static_assert(!std::is_invocable_v<RangeREndT, REndMemberReturnsVoidPtr const&>);
+
+struct PtrConvertible {
+ operator int*() const;
+};
+struct PtrConvertibleREndMember {
+ PtrConvertible rbegin() const;
+ PtrConvertible rend() const;
+};
+static_assert(!std::is_invocable_v<RangeREndT, PtrConvertibleREndMember const&>);
+
+struct NoRBeginMember {
+ constexpr const int* rend();
+};
+static_assert(!std::is_invocable_v<RangeREndT, NoRBeginMember const&>);
+
+struct NonConstREndMember {
+ int x;
+ constexpr int* rbegin() { return nullptr; }
+ constexpr int* rend() { return &x; }
+};
+static_assert( std::is_invocable_v<RangeREndT, NonConstREndMember &>);
+static_assert(!std::is_invocable_v<RangeREndT, NonConstREndMember const&>);
+static_assert(!std::is_invocable_v<RangeCREndT, NonConstREndMember &>);
+static_assert(!std::is_invocable_v<RangeCREndT, NonConstREndMember const&>);
+
+struct EnabledBorrowingREndMember {
+ constexpr int* rbegin() const { return nullptr; }
+ constexpr int* rend() const { return &globalBuff[0]; }
+};
+
+template <>
+inline constexpr bool std::ranges::enable_borrowed_range<EnabledBorrowingREndMember> = 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<RangeREndT, EmptyEndMember const&>);
+
+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<RangeCREndT, decltype((b))>);
+
+ 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<RangeREndT, REndFunction const&>);
+static_assert(!std::is_invocable_v<RangeREndT, REndFunction &&>);
+
+static_assert( std::is_invocable_v<RangeREndT, REndFunction const&>);
+static_assert(!std::is_invocable_v<RangeREndT, REndFunction &&>);
+static_assert(!std::is_invocable_v<RangeREndT, REndFunction &>);
+static_assert( std::is_invocable_v<RangeCREndT, REndFunction const&>);
+static_assert( std::is_invocable_v<RangeCREndT, REndFunction &>);
+
+struct REndFunctionReturnsInt {
+ friend constexpr int rbegin(REndFunctionReturnsInt const&);
+ friend constexpr int rend(REndFunctionReturnsInt const&);
+};
+static_assert(!std::is_invocable_v<RangeREndT, REndFunctionReturnsInt const&>);
+
+struct REndFunctionReturnsVoidPtr {
+ friend constexpr void* rbegin(REndFunctionReturnsVoidPtr const&);
+ friend constexpr void* rend(REndFunctionReturnsVoidPtr const&);
+};
+static_assert(!std::is_invocable_v<RangeREndT, REndFunctionReturnsVoidPtr const&>);
+
+struct REndFunctionReturnsEmpty {
+ friend constexpr Empty rbegin(REndFunctionReturnsEmpty const&);
+ friend constexpr Empty rend(REndFunctionReturnsEmpty const&);
+};
+static_assert(!std::is_invocable_v<RangeREndT, REndFunctionReturnsEmpty const&>);
+
+struct REndFunctionReturnsPtrConvertible {
+ friend constexpr PtrConvertible rbegin(REndFunctionReturnsPtrConvertible const&);
+ friend constexpr PtrConvertible rend(REndFunctionReturnsPtrConvertible const&);
+};
+static_assert(!std::is_invocable_v<RangeREndT, REndFunctionReturnsPtrConvertible const&>);
+
+struct NoRBeginFunction {
+ friend constexpr const int* rend(NoRBeginFunction const&);
+};
+static_assert(!std::is_invocable_v<RangeREndT, NoRBeginFunction const&>);
+
+struct REndFunctionByValue {
+ friend constexpr int* rbegin(REndFunctionByValue) { return nullptr; }
+ friend constexpr int* rend(REndFunctionByValue) { return &globalBuff[1]; }
+};
+static_assert(!std::is_invocable_v<RangeCREndT, REndFunctionByValue>);
+
+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<REndFunctionEnabledBorrowing> = 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<RangeREndT, decltype((aa))>);
+ 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<RangeREndT, decltype((dd))>);
+ 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<RangeREndT, decltype((ee))>);
+ 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<RangeREndT, decltype((ff))>);
+ 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<RangeREndT, decltype((gg))>);
+ assert(std::ranges::crend(gg) == &gg.x);
+
+ return true;
+}
+
+
+struct MemberBeginEnd {
+ int b, e;
+ char cb, ce;
+ constexpr bidirectional_iterator<int*> begin() { return bidirectional_iterator<int*>(&b); }
+ constexpr bidirectional_iterator<int*> end() { return bidirectional_iterator<int*>(&e); }
+ constexpr bidirectional_iterator<const char*> begin() const { return bidirectional_iterator<const char*>(&cb); }
+ constexpr bidirectional_iterator<const char*> end() const { return bidirectional_iterator<const char*>(&ce); }
+};
+static_assert( std::is_invocable_v<RangeREndT, MemberBeginEnd&>);
+static_assert( std::is_invocable_v<RangeREndT, MemberBeginEnd const&>);
+static_assert( std::is_invocable_v<RangeCREndT, MemberBeginEnd const&>);
+
+struct FunctionBeginEnd {
+ int b, e;
+ char cb, ce;
+ friend constexpr bidirectional_iterator<int*> begin(FunctionBeginEnd& v) {
+ return bidirectional_iterator<int*>(&v.b);
+ }
+ friend constexpr bidirectional_iterator<int*> end(FunctionBeginEnd& v) { return bidirectional_iterator<int*>(&v.e); }
+ friend constexpr bidirectional_iterator<const char*> begin(const FunctionBeginEnd& v) {
+ return bidirectional_iterator<const char*>(&v.cb);
+ }
+ friend constexpr bidirectional_iterator<const char*> end(const FunctionBeginEnd& v) {
+ return bidirectional_iterator<const char*>(&v.ce);
+ }
+};
+static_assert( std::is_invocable_v<RangeREndT, FunctionBeginEnd&>);
+static_assert( std::is_invocable_v<RangeREndT, FunctionBeginEnd const&>);
+static_assert( std::is_invocable_v<RangeCREndT, FunctionBeginEnd const&>);
+
+struct MemberBeginFunctionEnd {
+ int b, e;
+ char cb, ce;
+ constexpr bidirectional_iterator<int*> begin() { return bidirectional_iterator<int*>(&b); }
+ friend constexpr bidirectional_iterator<int*> end(MemberBeginFunctionEnd& v) {
+ return bidirectional_iterator<int*>(&v.e);
+ }
+ constexpr bidirectional_iterator<const char*> begin() const { return bidirectional_iterator<const char*>(&cb); }
+ friend constexpr bidirectional_iterator<const char*> end(const MemberBeginFunctionEnd& v) {
+ return bidirectional_iterator<const char*>(&v.ce);
+ }
+};
+static_assert( std::is_invocable_v<RangeREndT, MemberBeginFunctionEnd&>);
+static_assert( std::is_invocable_v<RangeREndT, MemberBeginFunctionEnd const&>);
+static_assert( std::is_invocable_v<RangeCREndT, MemberBeginFunctionEnd const&>);
+
+struct FunctionBeginMemberEnd {
+ int b, e;
+ char cb, ce;
+ friend constexpr bidirectional_iterator<int*> begin(FunctionBeginMemberEnd& v) {
+ return bidirectional_iterator<int*>(&v.b);
+ }
+ constexpr bidirectional_iterator<int*> end() { return bidirectional_iterator<int*>(&e); }
+ friend constexpr bidirectional_iterator<const char*> begin(const FunctionBeginMemberEnd& v) {
+ return bidirectional_iterator<const char*>(&v.cb);
+ }
+ constexpr bidirectional_iterator<const char*> end() const { return bidirectional_iterator<const char*>(&ce); }
+};
+static_assert( std::is_invocable_v<RangeREndT, FunctionBeginMemberEnd&>);
+static_assert( std::is_invocable_v<RangeREndT, FunctionBeginMemberEnd const&>);
+static_assert( std::is_invocable_v<RangeCREndT, FunctionBeginMemberEnd const&>);
+
+struct MemberBeginEndDifferentTypes {
+ bidirectional_iterator<int*> begin();
+ bidirectional_iterator<const int*> end();
+};
+static_assert(!std::is_invocable_v<RangeREndT, MemberBeginEndDifferentTypes&>);
+static_assert(!std::is_invocable_v<RangeCREndT, MemberBeginEndDifferentTypes&>);
+
+struct FunctionBeginEndDifferentTypes {
+ friend bidirectional_iterator<int*> begin(FunctionBeginEndDifferentTypes&);
+ friend bidirectional_iterator<const int*> end(FunctionBeginEndDifferentTypes&);
+};
+static_assert(!std::is_invocable_v<RangeREndT, FunctionBeginEndDifferentTypes&>);
+static_assert(!std::is_invocable_v<RangeCREndT, FunctionBeginEndDifferentTypes&>);
+
+struct MemberBeginEndForwardIterators {
+ forward_iterator<int*> begin();
+ forward_iterator<int*> end();
+};
+static_assert(!std::is_invocable_v<RangeREndT, MemberBeginEndForwardIterators&>);
+static_assert(!std::is_invocable_v<RangeCREndT, MemberBeginEndForwardIterators&>);
+
+struct FunctionBeginEndForwardIterators {
+ friend forward_iterator<int*> begin(FunctionBeginEndForwardIterators&);
+ friend forward_iterator<int*> end(FunctionBeginEndForwardIterators&);
+};
+static_assert(!std::is_invocable_v<RangeREndT, FunctionBeginEndForwardIterators&>);
+static_assert(!std::is_invocable_v<RangeCREndT, FunctionBeginEndForwardIterators&>);
+
+struct MemberBeginOnly {
+ bidirectional_iterator<int*> begin() const;
+};
+static_assert(!std::is_invocable_v<RangeREndT, MemberBeginOnly&>);
+static_assert(!std::is_invocable_v<RangeCREndT, MemberBeginOnly&>);
+
+struct FunctionBeginOnly {
+ friend bidirectional_iterator<int*> begin(FunctionBeginOnly&);
+};
+static_assert(!std::is_invocable_v<RangeREndT, FunctionBeginOnly&>);
+static_assert(!std::is_invocable_v<RangeCREndT, FunctionBeginOnly&>);
+
+struct MemberEndOnly {
+ bidirectional_iterator<int*> end() const;
+};
+static_assert(!std::is_invocable_v<RangeREndT, MemberEndOnly&>);
+static_assert(!std::is_invocable_v<RangeCREndT, MemberEndOnly&>);
+
+struct FunctionEndOnly {
+ friend bidirectional_iterator<int*> end(FunctionEndOnly&);
+};
+static_assert(!std::is_invocable_v<RangeREndT, FunctionEndOnly&>);
+static_assert(!std::is_invocable_v<RangeCREndT, FunctionEndOnly&>);
+
+// 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<RangeREndT, MemberBeginAndRBegin&>);
+static_assert( std::is_invocable_v<RangeCREndT, MemberBeginAndRBegin&>);
+static_assert( std::same_as<std::invoke_result_t<RangeREndT, MemberBeginAndRBegin&>, int*>);
+static_assert( std::same_as<std::invoke_result_t<RangeCREndT, MemberBeginAndRBegin&>, 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;
+}
+
+
+ASSERT_NOEXCEPT(std::ranges::rend(std::declval<int (&)[10]>()));
+ASSERT_NOEXCEPT(std::ranges::crend(std::declval<int (&)[10]>()));
+
+struct NoThrowMemberREnd {
+ ThrowingIterator<int> rbegin() const;
+ ThrowingIterator<int> 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<int> rbegin() const;
+ friend ThrowingIterator<int> rend(NoThrowADLREnd&) noexcept; // auto(rend(t)) doesn't throw
+ friend ThrowingIterator<int> rend(const NoThrowADLREnd&) noexcept;
+} ntare;
+static_assert(noexcept(std::ranges::rend(ntare)));
+static_assert(noexcept(std::ranges::crend(ntare)));
+
+struct NoThrowMemberREndReturnsRef {
+ ThrowingIterator<int> rbegin() const;
+ ThrowingIterator<int>& 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 NoThrowBeginThrowingEnd {
+ int* begin() const noexcept;
+ int* end() const;
+} ntbte;
+static_assert(noexcept(std::ranges::rend(ntbte)));
+static_assert(noexcept(std::ranges::crend(ntbte)));
+
+struct NoThrowEndThrowingBegin {
+ int* begin() const;
+ int* end() const noexcept;
+} ntetb;
+static_assert(!noexcept(std::ranges::rend(ntetb)));
+static_assert(!noexcept(std::ranges::crend(ntetb)));
+
+// Test ADL-proofing.
+struct Incomplete;
+template<class T> struct Holder { T t; };
+static_assert(!std::is_invocable_v<RangeREndT, Holder<Incomplete>*>);
+static_assert(!std::is_invocable_v<RangeREndT, Holder<Incomplete>*&>);
+static_assert(!std::is_invocable_v<RangeCREndT, Holder<Incomplete>*>);
+static_assert(!std::is_invocable_v<RangeCREndT, Holder<Incomplete>*&>);
+
+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;
+}
More information about the libcxx-commits
mailing list