[libcxx-commits] [libcxx] [libcxx] P2278R4: implement `{basic_, }const_iterator`, and have `cbegin` et. al. return it (PR #99915)

nicole mazzuca via libcxx-commits libcxx-commits at lists.llvm.org
Sat Jul 27 01:36:24 PDT 2024


https://github.com/strega-nil updated https://github.com/llvm/llvm-project/pull/99915

>From e58981c3b068625383c96e813c0f6cf6653bab0d Mon Sep 17 00:00:00 2001
From: Nicole Mazzuca <nicole at strega-nil.co>
Date: Mon, 22 Jul 2024 15:39:27 +0200
Subject: [PATCH 1/4] [libcxx] P2278R4: const_iterator and c{begin,end}

Implements `basic_const_iterator` and the const accessors.

Does not implement `const_view`

Does not implement changes to `span` and friends.
---
 libcxx/docs/Status/Cxx23Papers.csv         |   2 +-
 libcxx/include/CMakeLists.txt              |   2 +
 libcxx/include/__iterator/const_iterator.h | 328 +++++++++++++++++++++
 libcxx/include/__ranges/access.h           |  55 ----
 libcxx/include/__ranges/concepts.h         |  14 +
 libcxx/include/__ranges/const_access.h     | 202 +++++++++++++
 libcxx/include/__ranges/rbegin.h           |  28 --
 libcxx/include/__ranges/rend.h             |  27 --
 libcxx/include/iterator                    |  29 ++
 libcxx/include/ranges                      |   8 +
 10 files changed, 584 insertions(+), 111 deletions(-)
 create mode 100644 libcxx/include/__iterator/const_iterator.h
 create mode 100644 libcxx/include/__ranges/const_access.h

diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index f8970320ce1fa..2d294e8501e5b 100644
--- a/libcxx/docs/Status/Cxx23Papers.csv
+++ b/libcxx/docs/Status/Cxx23Papers.csv
@@ -61,7 +61,7 @@
 "`P1899R3 <https://wg21.link/P1899R3>`__","LWG","``stride_view``","July 2022","","","|ranges|"
 "`P2093R14 <https://wg21.link/P2093R14>`__","LWG","Formatted output","July 2022","|Complete|","18.0","|format|"
 "`P2165R4 <https://wg21.link/P2165R4>`__","LWG","Compatibility between ``tuple``, ``pair`` and ``tuple-like`` objects","July 2022","",""
-"`P2278R4 <https://wg21.link/P2278R4>`__","LWG","``cbegin`` should always return a constant iterator","July 2022","","","|ranges|"
+"`P2278R4 <https://wg21.link/P2278R4>`__","LWG","``cbegin`` should always return a constant iterator","July 2022","In Progress","","|ranges|"
 "`P2286R8 <https://wg21.link/P2286R8>`__","LWG","Formatting Ranges","July 2022","|Complete|","16.0","|format| |ranges|"
 "`P2291R3 <https://wg21.link/P2291R3>`__","LWG","Add Constexpr Modifiers to Functions ``to_chars`` and ``from_chars`` for Integral Types in ``<charconv>`` Header","July 2022","|Complete|","16.0"
 "`P2302R4 <https://wg21.link/P2302R4>`__","LWG","``std::ranges::contains``","July 2022","|Complete|","19.0","|ranges|"
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 32579272858a8..577b7975f3eb2 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -444,6 +444,7 @@ set(files
   __iterator/bounded_iter.h
   __iterator/common_iterator.h
   __iterator/concepts.h
+  __iterator/const_iterator.h
   __iterator/counted_iterator.h
   __iterator/cpp17_iterator_concepts.h
   __iterator/data.h
@@ -639,6 +640,7 @@ set(files
   __ranges/chunk_by_view.h
   __ranges/common_view.h
   __ranges/concepts.h
+  __ranges/const_access.h
   __ranges/container_compatible_range.h
   __ranges/counted.h
   __ranges/dangling.h
diff --git a/libcxx/include/__iterator/const_iterator.h b/libcxx/include/__iterator/const_iterator.h
new file mode 100644
index 0000000000000..7549a98fe3ecc
--- /dev/null
+++ b/libcxx/include/__iterator/const_iterator.h
@@ -0,0 +1,328 @@
+// -*- 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___ITERATOR_CONST_ITERATOR_H
+#define _LIBCPP___ITERATOR_CONST_ITERATOR_H
+
+#include <__compare/three_way_comparable.h>
+#include <__concepts/common_with.h>
+#include <__concepts/different_from.h>
+#include <__iterator/concepts.h>
+#include <__iterator/iterator_traits.h>
+#include <__type_traits/common_reference.h>
+#include <__type_traits/common_type.h>
+#include <__type_traits/integral_constant.h>
+#include <__type_traits/is_specialization.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 23
+
+template <indirectly_readable _Iter>
+using iter_const_reference_t = common_reference_t<const iter_value_t<_Iter>&&, iter_reference_t<_Iter>>;
+
+template <class _Iter>
+concept __constant_iterator = input_iterator<_Iter> && same_as<iter_const_reference_t<_Iter>, iter_reference_t<_Iter>>;
+
+template <input_iterator _Iter>
+class basic_const_iterator;
+
+template <input_iterator _Iter>
+using const_iterator = conditional_t<__constant_iterator<_Iter>, _Iter, basic_const_iterator<_Iter>>;
+template <semiregular _Sent>
+using const_sentinel = conditional_t<input_iterator<_Sent>, const_iterator<_Sent>, _Sent>;
+
+template <class _Iter>
+concept __not_a_const_iterator = !__is_specialization_v<_Iter, basic_const_iterator>;
+
+template <indirectly_readable _Iter>
+using __iter_const_rvalue_reference_t = common_reference_t<const iter_value_t<_Iter>&&, iter_rvalue_reference_t<_Iter>>;
+
+template <class _Iter>
+struct __basic_const_iterator_concept {
+  // clang-format off
+  using iterator_concept =
+    conditional_t<contiguous_iterator<_Iter>,
+      contiguous_iterator_tag,
+    conditional_t<random_access_iterator<_Iter>,
+      random_access_iterator_tag,
+    conditional_t<bidirectional_iterator<_Iter>,
+      bidirectional_iterator_tag,
+    conditional_t<forward_iterator<_Iter>,
+      forward_iterator_tag,
+    // else
+      input_iterator_tag>>>>;
+  // clang-format on
+};
+
+template <class _Iter>
+struct __basic_const_iterator_category : __basic_const_iterator_concept<_Iter> {};
+template <forward_iterator _Iter>
+struct __basic_const_iterator_category<_Iter> : __basic_const_iterator_concept<_Iter> {
+  using iterator_category = __basic_const_iterator_concept<_Iter>::iterator_concept;
+};
+
+template <input_iterator _Iter>
+class _LIBCPP_TEMPLATE_VIS basic_const_iterator : __basic_const_iterator_category<_Iter> {
+  _Iter __current = _Iter();
+
+  using __reference        = iter_const_reference_t<_Iter>;
+  using __rvalue_reference = __iter_const_rvalue_reference_t<_Iter>;
+
+public:
+  using value_type      = iter_value_t<_Iter>;
+  using difference_type = iter_difference_t<_Iter>;
+
+  _LIBCPP_HIDE_FROM_ABI basic_const_iterator()
+    requires default_initializable<_Iter>
+  = default;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr basic_const_iterator(_Iter __cur) : __current(std::move(__cur)) {}
+  template <convertible_to<_Iter> _Type>
+  _LIBCPP_HIDE_FROM_ABI constexpr basic_const_iterator(basic_const_iterator<_Type> __cur)
+      : __current(std::move(__cur.__current)) {}
+  template <__different_from<basic_const_iterator> _Type>
+    requires convertible_to<_Type, _Iter>
+  _LIBCPP_HIDE_FROM_ABI constexpr basic_const_iterator(_Type&& __cur) : __current(std::forward<_Type>(__cur)) {}
+
+  _LIBCPP_HIDE_FROM_ABI constexpr const _Iter& base() const& noexcept { return __current; }
+  _LIBCPP_HIDE_FROM_ABI constexpr _Iter base() && { return std::move(__current); }
+
+  constexpr __reference operator*() const { return static_cast<__reference>(*__current); }
+  _LIBCPP_HIDE_FROM_ABI constexpr const auto* operator->() const
+    requires is_lvalue_reference_v<iter_reference_t<_Iter>> &&
+             same_as<remove_cvref_t<iter_reference_t<_Iter>>, value_type>
+  {
+    if constexpr (contiguous_iterator<_Iter>) {
+      return std::to_address(__current);
+    } else {
+      return std::addressof(*__current);
+    }
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr basic_const_iterator& operator++() {
+    ++__current;
+    return *this;
+  }
+  constexpr void operator++(int) { ++__current; }
+  _LIBCPP_HIDE_FROM_ABI constexpr basic_const_iterator operator++(int)
+    requires forward_iterator<_Iter>
+  {
+    auto __tmp = *this;
+    ++__current;
+    return __tmp;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr basic_const_iterator& operator--()
+    requires bidirectional_iterator<_Iter>
+  {
+    --__current;
+    return *this;
+  }
+  _LIBCPP_HIDE_FROM_ABI constexpr basic_const_iterator operator--(int)
+    requires bidirectional_iterator<_Iter>
+  {
+    auto __tmp = *this;
+    --__current;
+    return __tmp;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr basic_const_iterator& operator+=(difference_type __n)
+    requires random_access_iterator<_Iter>
+  {
+    __current += __n;
+    return *this;
+  }
+  _LIBCPP_HIDE_FROM_ABI constexpr basic_const_iterator& operator-=(difference_type __n)
+    requires random_access_iterator<_Iter>
+  {
+    __current -= __n;
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __reference operator[](difference_type __n) const
+    requires random_access_iterator<_Iter>
+  {
+    return static_cast<__reference>(__current[__n]);
+  }
+
+  template <sentinel_for<_Iter> _Sent>
+  _LIBCPP_HIDE_FROM_ABI constexpr bool operator==(const _Sent& __sent) const {
+    return __current == __sent;
+  }
+
+  template <__not_a_const_iterator _ConstIt>
+    requires __constant_iterator<_ConstIt> && convertible_to<_Iter const&, _ConstIt>
+  _LIBCPP_HIDE_FROM_ABI constexpr operator _ConstIt() const& {
+    return __current;
+  }
+  template <__not_a_const_iterator _ConstIt>
+    requires __constant_iterator<_ConstIt> && convertible_to<_Iter, _ConstIt>
+  _LIBCPP_HIDE_FROM_ABI constexpr operator _ConstIt() && {
+    return std::move(__current);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr bool operator<(const basic_const_iterator& __rhs) const
+    requires random_access_iterator<_Iter>
+  {
+    return __current < __rhs.__current;
+  }
+  _LIBCPP_HIDE_FROM_ABI constexpr bool operator>(const basic_const_iterator& __rhs) const
+    requires random_access_iterator<_Iter>
+  {
+    return __current > __rhs.__current;
+  }
+  _LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const basic_const_iterator& __rhs) const
+    requires random_access_iterator<_Iter>
+  {
+    return __current <= __rhs.__current;
+  }
+  _LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(const basic_const_iterator& __rhs) const
+    requires random_access_iterator<_Iter>
+  {
+    return __current >= __rhs.__current;
+  }
+  _LIBCPP_HIDE_FROM_ABI constexpr auto operator<=>(const basic_const_iterator& __rhs) const
+    requires random_access_iterator<_Iter> && three_way_comparable<_Iter>
+  {
+    return __current <=> __rhs.__current;
+  }
+
+  template <__different_from<basic_const_iterator> _Iter2>
+  _LIBCPP_HIDE_FROM_ABI constexpr bool operator<(const _Iter2& __rhs) const
+    requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Iter2>
+  {
+    return __current < __rhs.__current;
+  }
+  template <__different_from<basic_const_iterator> _Iter2>
+  _LIBCPP_HIDE_FROM_ABI constexpr bool operator>(const _Iter2& __rhs) const
+    requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Iter2>
+  {
+    return __current > __rhs.__current;
+  }
+  template <__different_from<basic_const_iterator> _Iter2>
+  _LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const _Iter2& __rhs) const
+    requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Iter2>
+  {
+    return __current <= __rhs.__current;
+  }
+  template <__different_from<basic_const_iterator> _Iter2>
+  _LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(const _Iter2& __rhs) const
+    requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Iter2>
+  {
+    return __current >= __rhs.__current;
+  }
+  template <__different_from<basic_const_iterator> _Iter2>
+  _LIBCPP_HIDE_FROM_ABI constexpr auto operator<=>(const _Iter2& __rhs) const
+    requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Iter2> &&
+             three_way_comparable_with<_Iter, _Iter2>
+  {
+    return __current <=> __rhs.__current;
+  }
+
+  template <__not_a_const_iterator _Iter2>
+  friend _LIBCPP_HIDE_FROM_ABI constexpr bool operator<(const _Iter2& __lhs, const basic_const_iterator& __rhs)
+    requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Iter2>
+  {
+    return __lhs < __rhs.__current;
+  }
+  template <__not_a_const_iterator _Iter2>
+  friend _LIBCPP_HIDE_FROM_ABI constexpr bool operator>(const _Iter2& __lhs, const basic_const_iterator& __rhs)
+    requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Iter2>
+  {
+    return __lhs > __rhs.__current;
+  }
+  template <__not_a_const_iterator _Iter2>
+  friend _LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const _Iter2& __lhs, const basic_const_iterator& __rhs)
+    requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Iter2>
+  {
+    return __lhs <= __rhs.__current;
+  }
+  template <__not_a_const_iterator _Iter2>
+  friend _LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(const _Iter2& __lhs, const basic_const_iterator& __rhs)
+    requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Iter2>
+  {
+    return __lhs >= __rhs.__current;
+  }
+
+  friend _LIBCPP_HIDE_FROM_ABI constexpr basic_const_iterator
+  operator+(const basic_const_iterator& __it, difference_type __n)
+    requires random_access_iterator<_Iter>
+  {
+    return basic_const_iterator(__it.__current + __n);
+  }
+  friend _LIBCPP_HIDE_FROM_ABI constexpr basic_const_iterator
+  operator+(difference_type __n, const basic_const_iterator& __it)
+    requires random_access_iterator<_Iter>
+  {
+    return basic_const_iterator(__it.__current + __n);
+  }
+
+  friend _LIBCPP_HIDE_FROM_ABI constexpr basic_const_iterator
+  operator-(const basic_const_iterator& __it, difference_type __n)
+    requires random_access_iterator<_Iter>
+  {
+    return basic_const_iterator(__it.__current - __n);
+  }
+  template <sized_sentinel_for<_Iter> _Sent>
+  _LIBCPP_HIDE_FROM_ABI constexpr difference_type operator-(const _Sent& __rhs) const {
+    return __current - __rhs;
+  }
+  template <__not_a_const_iterator _Sent>
+    requires sized_sentinel_for<_Sent, _Iter>
+  friend _LIBCPP_HIDE_FROM_ABI constexpr difference_type
+  operator-(const _Sent& __lhs, const basic_const_iterator& __rhs) {
+    return __lhs - __rhs;
+  }
+
+  friend _LIBCPP_HIDE_FROM_ABI constexpr __rvalue_reference iter_move(const basic_const_iterator& __it) noexcept(
+      noexcept(static_cast<__rvalue_reference>(ranges::iter_move(__it.current_)))) {
+    return static_cast<__rvalue_reference>(ranges::iter_move(__it.current_));
+  }
+};
+
+template <class _Type1, common_with<_Type1> _Type2>
+  requires input_iterator<common_type_t<_Type1, _Type2>>
+struct common_type<basic_const_iterator<_Type1>, _Type2> {
+  using type = basic_const_iterator<common_type_t<_Type1, _Type2>>;
+};
+template <class _Type1, common_with<_Type1> _Type2>
+  requires input_iterator<common_type_t<_Type1, _Type2>>
+struct common_type<_Type2, basic_const_iterator<_Type1>> {
+  using type = basic_const_iterator<common_type_t<_Type1, _Type2>>;
+};
+template <class _Type1, common_with<_Type1> _Type2>
+  requires input_iterator<common_type_t<_Type1, _Type2>>
+struct common_type<basic_const_iterator<_Type1>, basic_const_iterator<_Type2>> {
+  using type = basic_const_iterator<common_type_t<_Type1, _Type2>>;
+};
+
+template <input_iterator _Iter>
+_LIBCPP_HIDE_FROM_ABI constexpr const_iterator<_Iter> make_const_iterator(_Iter __it) {
+  return __it;
+}
+template <semiregular _Sent>
+_LIBCPP_HIDE_FROM_ABI constexpr const_sentinel<_Sent> make_const_sentinel(_Sent __sent) {
+  return __sent;
+}
+
+#endif // _LIBCPP_STD_VER >= 23
+
+_LIBCPP_END_NAMESPACE_STD
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___ITERATOR_CONST_ITERATOR_H
diff --git a/libcxx/include/__ranges/access.h b/libcxx/include/__ranges/access.h
index c0a40c5e10178..7955b3b2166dc 100644
--- a/libcxx/include/__ranges/access.h
+++ b/libcxx/include/__ranges/access.h
@@ -148,61 +148,6 @@ inline constexpr auto end = __end::__fn{};
 } // namespace __cpo
 } // namespace ranges
 
-// [range.access.cbegin]
-
-namespace ranges {
-namespace __cbegin {
-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::begin(static_cast<const remove_reference_t<_Tp>&>(__t))))
-          -> decltype(ranges::begin(static_cast<const remove_reference_t<_Tp>&>(__t))) {
-    return ranges::begin(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::begin(static_cast<const _Tp&&>(__t))))
-          -> decltype(ranges::begin(static_cast<const _Tp&&>(__t))) {
-    return ranges::begin(static_cast<const _Tp&&>(__t));
-  }
-};
-} // namespace __cbegin
-
-inline namespace __cpo {
-inline constexpr auto cbegin = __cbegin::__fn{};
-} // namespace __cpo
-} // namespace ranges
-
-// [range.access.cend]
-
-namespace ranges {
-namespace __cend {
-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::end(static_cast<const remove_reference_t<_Tp>&>(__t))))
-          -> decltype(ranges::end(static_cast<const remove_reference_t<_Tp>&>(__t))) {
-    return ranges::end(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::end(static_cast<const _Tp&&>(__t)))) -> decltype(ranges::end(static_cast<const _Tp&&>(__t))) {
-    return ranges::end(static_cast<const _Tp&&>(__t));
-  }
-};
-} // namespace __cend
-
-inline namespace __cpo {
-inline constexpr auto cend = __cend::__fn{};
-} // namespace __cpo
-} // namespace ranges
-
 #endif // _LIBCPP_STD_VER >= 20
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__ranges/concepts.h b/libcxx/include/__ranges/concepts.h
index 674a3f359ff99..19264d4ac76c3 100644
--- a/libcxx/include/__ranges/concepts.h
+++ b/libcxx/include/__ranges/concepts.h
@@ -15,6 +15,7 @@
 #include <__concepts/same_as.h>
 #include <__config>
 #include <__iterator/concepts.h>
+#include <__iterator/const_iterator.h>
 #include <__iterator/incrementable_traits.h>
 #include <__iterator/iter_move.h>
 #include <__iterator/iterator_traits.h>
@@ -61,6 +62,11 @@ concept borrowed_range =
 template <range _Rp>
 using sentinel_t = decltype(ranges::end(std::declval<_Rp&>()));
 
+#  if _LIBCPP_STD_VER >= 23
+template <range _Rp>
+using const_iterator_t = const_iterator<iterator_t<_Rp>>;
+#  endif // _LIBCPP_STD_VER >= 23
+
 template <range _Rp>
 using range_difference_t = iter_difference_t<iterator_t<_Rp>>;
 
@@ -70,6 +76,9 @@ using range_value_t = iter_value_t<iterator_t<_Rp>>;
 template <range _Rp>
 using range_reference_t = iter_reference_t<iterator_t<_Rp>>;
 
+template <range _Rp>
+using range_const_reference_t = iter_const_reference_t<iterator_t<_Rp>>;
+
 template <range _Rp>
 using range_rvalue_reference_t = iter_rvalue_reference_t<iterator_t<_Rp>>;
 
@@ -133,6 +142,11 @@ concept viewable_range =
       (is_lvalue_reference_v<_Tp> ||
        (movable<remove_reference_t<_Tp>> && !__is_std_initializer_list<remove_cvref_t<_Tp>>))));
 
+#  if _LIBCPP_STD_VER >= 23
+template <class _Tp>
+concept constant_range = input_range<_Tp> && __constant_iterator<iterator_t<_Tp>>;
+#  endif // _LIBCPP_STD_VER >= 23
+
 } // namespace ranges
 
 #endif // _LIBCPP_STD_VER >= 20
diff --git a/libcxx/include/__ranges/const_access.h b/libcxx/include/__ranges/const_access.h
new file mode 100644
index 0000000000000..4bc14e0049b71
--- /dev/null
+++ b/libcxx/include/__ranges/const_access.h
@@ -0,0 +1,202 @@
+// -*- 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_CONST_ACCESS_H
+#define _LIBCPP___RANGES_CONST_ACCESS_H
+
+#include <__iterator/const_iterator.h>
+#include <__ranges/access.h>
+#include <__ranges/concepts.h>
+#include <__ranges/enable_borrowed_range.h>
+#include <__ranges/rbegin.h>
+#include <__ranges/rend.h>
+#include <__type_traits/is_reference.h>
+#include <__type_traits/remove_reference.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 20
+
+// [range.const]
+#  if _LIBCPP_STD_VER >= 23
+namespace ranges {
+template <input_range _Rp>
+constexpr auto& __possibly_const_range(_Rp& __rng) {
+  if constexpr (constant_range<const _Rp> && !constant_range<_Rp>) {
+    return const_cast<const _Rp&>(__rng);
+  } else {
+    return __rng;
+  }
+}
+} // namespace ranges
+#  endif // _LIBCPP_STD_VER >= 23
+
+// [range.access.cbegin]
+namespace ranges {
+
+template <class _Type>
+concept __const_accessible_range = (!is_rvalue_reference_v<_Type&&> || enable_borrowed_range<remove_cv_t<_Type>>);
+
+namespace __cbegin {
+struct __fn {
+#  if _LIBCPP_STD_VER >= 23
+  template <class _Rng>
+  using _UType = decltype(ranges::begin(ranges::__possibly_const_range(std::declval<_Rng>())));
+
+  template <__const_accessible_range _Rng>
+  auto operator()(_Rng&& __rng) noexcept(noexcept(const_iterator<_UType<_Rng>>(
+      ranges::begin(ranges::__possibly_const_range(std::declval<_Rng>()))))) -> const_iterator<_UType<_Rng>> {
+    return const_iterator<_UType<_Rng>>(ranges::begin(ranges::__possibly_const_range(__rng)));
+  }
+#  else  // ^^^ _LIBCPP_STD_VER >= 23 / _LIBCPP_STD_VER < 23 vvv
+  template <class _Tp>
+    requires is_lvalue_reference_v<_Tp&&>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const
+      noexcept(noexcept(ranges::begin(static_cast<const remove_reference_t<_Tp>&>(__t))))
+          -> decltype(ranges::begin(static_cast<const remove_reference_t<_Tp>&>(__t))) {
+    return ranges::begin(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::begin(static_cast<const _Tp&&>(__t))))
+          -> decltype(ranges::begin(static_cast<const _Tp&&>(__t))) {
+    return ranges::begin(static_cast<const _Tp&&>(__t));
+  }
+#  endif // ^^^ _LIBCPP_STD_VER < 23
+};
+} // namespace __cbegin
+
+inline namespace __cpo {
+inline constexpr auto cbegin = __cbegin::__fn{};
+} // namespace __cpo
+} // namespace ranges
+
+// [range.access.cend]
+namespace ranges {
+namespace __cend {
+struct __fn {
+#  if _LIBCPP_STD_VER >= 23
+  template <class _Rng>
+  using _UType = decltype(ranges::end(ranges::__possibly_const_range(std::declval<_Rng>())));
+
+  template <__const_accessible_range _Rng>
+  auto operator()(_Rng&& __rng) noexcept(noexcept(const_sentinel<_UType<_Rng>>(
+      ranges::end(ranges::__possibly_const_range(__rng))))) -> const_sentinel<_UType<_Rng>> {
+    return const_sentinel<_UType<_Rng>>(ranges::end(ranges::__possibly_const_range(__rng)));
+  }
+#  else // ^^^ _LIBCPP_STD_VER >= 23 / _LIBCPP_STD_VER < 23 vvv
+  template <class _Tp>
+    requires is_lvalue_reference_v<_Tp&&>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const
+      noexcept(noexcept(ranges::end(static_cast<const remove_reference_t<_Tp>&>(__t))))
+          -> decltype(ranges::end(static_cast<const remove_reference_t<_Tp>&>(__t))) {
+    return ranges::end(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::end(static_cast<const _Tp&&>(__t)))) -> decltype(ranges::end(static_cast<const _Tp&&>(__t))) {
+    return ranges::end(static_cast<const _Tp&&>(__t));
+  }
+#  endif
+};
+} // namespace __cend
+
+inline namespace __cpo {
+inline constexpr auto cend = __cend::__fn{};
+} // namespace __cpo
+} // namespace ranges
+
+// [range.access.crbegin]
+namespace ranges {
+namespace __crbegin {
+struct __fn {
+#  if _LIBCPP_STD_VER >= 23
+  template <class _Rng>
+  using _UType = decltype(ranges::rbegin(ranges::__possibly_const_range(std::declval<_Rng>())));
+
+  template <__const_accessible_range _Rng>
+  auto operator()(_Rng&& __rng) noexcept(noexcept(const_iterator<_UType<_Rng>>(
+      ranges::rbegin(ranges::__possibly_const_range(__rng))))) -> const_iterator<_UType<_Rng>> {
+    return const_iterator<_UType<_Rng>>(ranges::rbegin(ranges::__possibly_const_range(__rng)));
+  }
+#  else  // ^^^ _LIBCPP_STD_VER >= 23 / _LIBCPP_STD_VER < 23
+  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));
+  }
+#  endif // ^^^ _LIBCPP_STD_VER < 23
+};
+} // namespace __crbegin
+
+inline namespace __cpo {
+inline constexpr auto crbegin = __crbegin::__fn{};
+} // namespace __cpo
+} // namespace ranges
+
+// [range.access.crend]
+namespace ranges {
+namespace __crend {
+struct __fn {
+#  if _LIBCPP_STD_VER >= 23
+  template <class _Rng>
+  using _UType = decltype(ranges::rend(ranges::__possibly_const_range(std::declval<_Rng>())));
+
+  template <__const_accessible_range _Rng>
+  auto operator()(_Rng&& __rng) noexcept(noexcept(const_sentinel<_UType<_Rng>>(
+      ranges::rend(ranges::__possibly_const_range(__rng))))) -> const_sentinel<_UType<_Rng>> {
+    return const_sentinel<_UType<_Rng>>(ranges::rend(ranges::__possibly_const_range(__rng)));
+  }
+#  else  // ^^^ _LIBCPP_STD_VER >= 23 / _LIBCPP_STD_VER < 23 vvv
+  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));
+  }
+#  endif // ^^^ _LIBCPP_STD_VER < 23
+};
+} // namespace __crend
+
+inline namespace __cpo {
+inline constexpr auto crend = __crend::__fn{};
+} // namespace __cpo
+} // namespace ranges
+
+#endif // _LIBCPP_STD_VER >= 20
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___RANGES_CONST_ACCESS_H
diff --git a/libcxx/include/__ranges/rbegin.h b/libcxx/include/__ranges/rbegin.h
index 12e739e1a2b85..a6f7e2d9f1aea 100644
--- a/libcxx/include/__ranges/rbegin.h
+++ b/libcxx/include/__ranges/rbegin.h
@@ -85,34 +85,6 @@ 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 // _LIBCPP_STD_VER >= 20
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__ranges/rend.h b/libcxx/include/__ranges/rend.h
index 02b4c5999a7eb..5171cc6cf3434 100644
--- a/libcxx/include/__ranges/rend.h
+++ b/libcxx/include/__ranges/rend.h
@@ -89,33 +89,6 @@ 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 // _LIBCPP_STD_VER >= 20
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/iterator b/libcxx/include/iterator
index fca75f0a19ed1..9a0c618f860cf 100644
--- a/libcxx/include/iterator
+++ b/libcxx/include/iterator
@@ -383,6 +383,31 @@ insert_iterator<Container> inserter(Container& x, typename Container::iterator i
 template <class Container>
 constexpr insert_iterator<Container> inserter(Container& x, ranges::iterator_t<Container> i);  // since C++20
 
+template<indirectly_readable I>
+using iter_const_reference_t = see below;                                       // since C++23
+template<input_iterator I>
+using const_iterator = see below;                                               // since C++23
+template<semiregular S>
+using const_sentinel = see below;                                               // since C++23
+
+template<input_iterator Iterator>
+class basic_const_iterator;                                                     // since C++23
+
+template<class T, common_with<T> U>
+  requires input_iterator<common_type_t<T, U>>
+struct common_type<basic_const_iterator<T>, U>;                                 // since C++23
+template<class T, common_with<T> U>
+  requires input_iterator<common_type_t<T, U>>
+struct common_type<U, basic_const_iterator<T>>;                                 // since C++23
+template<class T, common_with<T> U>
+  requires input_iterator<common_type_t<T, U>>
+struct common_type<basic_const_iterator<T>, basic_const_iterator<U>>;           // since C++23
+
+template<input_iterator I>
+constexpr const_iterator<I> make_const_iterator(I it);                          // since C++23
+template<semiregular S>
+constexpr const_sentinel<S> make_const_sentinel(S s);                           // since C++23
+
 template <class Iterator>
 class move_iterator {
 public:
@@ -726,6 +751,10 @@ template <class E> constexpr const E* data(initializer_list<E> il) noexcept;
 #  include <__iterator/unreachable_sentinel.h>
 #endif
 
+#if _LIBCPP_STD_VER >= 23
+#  include <__iterator/const_iterator.h>
+#endif
+
 #include <version>
 
 // standard-mandated includes
diff --git a/libcxx/include/ranges b/libcxx/include/ranges
index fa35874265de6..7961862085023 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -39,6 +39,8 @@ namespace std::ranges {
     using iterator_t = decltype(ranges::begin(declval<T&>()));
   template<range R>
     using sentinel_t = decltype(ranges::end(declval<R&>()));
+  template<range R>
+    using const_iterator_t = decltype(ranges::end(declval<R&>()));               // since C++23
   template<range R>
     using range_difference_t = iter_difference_t<iterator_t<R>>;
   template<sized_range R>
@@ -47,6 +49,8 @@ namespace std::ranges {
     using range_value_t = iter_value_t<iterator_t<R>>;
   template<range R>
     using range_reference_t = iter_reference_t<iterator_t<R>>;
+  template<range R>
+    using range_const_reference_t = decltype(ranges::end(declval<R&>()));        // since C++23
   template<range R>
     using range_rvalue_reference_t = iter_rvalue_reference_t<iterator_t<R>>;
   template <range R>
@@ -93,6 +97,9 @@ namespace std::ranges {
   template<class T>
   concept viewable_range = see below;
 
+  template<class T>
+    concept constant_range = see below;
+
   // [range.adaptor.object], range adaptor objects
   template<class D>
     requires is_class_v<D> && same_as<D, remove_cv_t<D>>
@@ -387,6 +394,7 @@ namespace std {
 #  include <__ranges/all.h>
 #  include <__ranges/common_view.h>
 #  include <__ranges/concepts.h>
+#  include <__ranges/const_access.h>
 #  include <__ranges/counted.h>
 #  include <__ranges/dangling.h>
 #  include <__ranges/data.h>

>From 3b3c56d282a8d797118e5ec0157447404eef8dfd Mon Sep 17 00:00:00 2001
From: Nicole Mazzuca <nicole at strega-nil.co>
Date: Wed, 24 Jul 2024 10:33:46 +0200
Subject: [PATCH 2/4] [wip] start working on testing

---
 libcxx/include/__iterator/const_iterator.h    |  12 +
 libcxx/include/__ranges/const_access.h        |  12 +-
 libcxx/include/module.modulemap               |   2 +
 libcxx/modules/std/iterator.inc               |  15 +-
 .../std/ranges/range.access/begin.pass.cpp    |   2 +-
 .../test/std/ranges/range.access/end.pass.cpp |   2 +-
 .../std/ranges/range.access/rbegin.pass.cpp   | 233 +++++++++++-------
 .../std/ranges/range.access/rend.pass.cpp     | 195 ++++++++++-----
 8 files changed, 309 insertions(+), 164 deletions(-)

diff --git a/libcxx/include/__iterator/const_iterator.h b/libcxx/include/__iterator/const_iterator.h
index 7549a98fe3ecc..4ad37345cd887 100644
--- a/libcxx/include/__iterator/const_iterator.h
+++ b/libcxx/include/__iterator/const_iterator.h
@@ -12,13 +12,25 @@
 
 #include <__compare/three_way_comparable.h>
 #include <__concepts/common_with.h>
+#include <__concepts/convertible_to.h>
 #include <__concepts/different_from.h>
+#include <__concepts/same_as.h>
+#include <__concepts/semiregular.h>
+#include <__concepts/totally_ordered.h>
 #include <__iterator/concepts.h>
+#include <__iterator/incrementable_traits.h>
+#include <__iterator/iter_move.h>
 #include <__iterator/iterator_traits.h>
+#include <__memory/addressof.h>
+#include <__memory/pointer_traits.h>
 #include <__type_traits/common_reference.h>
 #include <__type_traits/common_type.h>
+#include <__type_traits/conditional.h>
 #include <__type_traits/integral_constant.h>
+#include <__type_traits/is_reference.h>
 #include <__type_traits/is_specialization.h>
+#include <__utility/forward.h>
+#include <__utility/move.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
diff --git a/libcxx/include/__ranges/const_access.h b/libcxx/include/__ranges/const_access.h
index 4bc14e0049b71..270bf7b2f1675 100644
--- a/libcxx/include/__ranges/const_access.h
+++ b/libcxx/include/__ranges/const_access.h
@@ -54,7 +54,8 @@ struct __fn {
   using _UType = decltype(ranges::begin(ranges::__possibly_const_range(std::declval<_Rng>())));
 
   template <__const_accessible_range _Rng>
-  auto operator()(_Rng&& __rng) noexcept(noexcept(const_iterator<_UType<_Rng>>(
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr static auto
+  operator()(_Rng&& __rng) noexcept(noexcept(const_iterator<_UType<_Rng>>(
       ranges::begin(ranges::__possibly_const_range(std::declval<_Rng>()))))) -> const_iterator<_UType<_Rng>> {
     return const_iterator<_UType<_Rng>>(ranges::begin(ranges::__possibly_const_range(__rng)));
   }
@@ -92,7 +93,8 @@ struct __fn {
   using _UType = decltype(ranges::end(ranges::__possibly_const_range(std::declval<_Rng>())));
 
   template <__const_accessible_range _Rng>
-  auto operator()(_Rng&& __rng) noexcept(noexcept(const_sentinel<_UType<_Rng>>(
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr static auto
+  operator()(_Rng&& __rng) noexcept(noexcept(const_sentinel<_UType<_Rng>>(
       ranges::end(ranges::__possibly_const_range(__rng))))) -> const_sentinel<_UType<_Rng>> {
     return const_sentinel<_UType<_Rng>>(ranges::end(ranges::__possibly_const_range(__rng)));
   }
@@ -129,7 +131,8 @@ struct __fn {
   using _UType = decltype(ranges::rbegin(ranges::__possibly_const_range(std::declval<_Rng>())));
 
   template <__const_accessible_range _Rng>
-  auto operator()(_Rng&& __rng) noexcept(noexcept(const_iterator<_UType<_Rng>>(
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr static auto
+  operator()(_Rng&& __rng) noexcept(noexcept(const_iterator<_UType<_Rng>>(
       ranges::rbegin(ranges::__possibly_const_range(__rng))))) -> const_iterator<_UType<_Rng>> {
     return const_iterator<_UType<_Rng>>(ranges::rbegin(ranges::__possibly_const_range(__rng)));
   }
@@ -167,7 +170,8 @@ struct __fn {
   using _UType = decltype(ranges::rend(ranges::__possibly_const_range(std::declval<_Rng>())));
 
   template <__const_accessible_range _Rng>
-  auto operator()(_Rng&& __rng) noexcept(noexcept(const_sentinel<_UType<_Rng>>(
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr static auto
+  operator()(_Rng&& __rng) noexcept(noexcept(const_sentinel<_UType<_Rng>>(
       ranges::rend(ranges::__possibly_const_range(__rng))))) -> const_sentinel<_UType<_Rng>> {
     return const_sentinel<_UType<_Rng>>(ranges::rend(ranges::__possibly_const_range(__rng)));
   }
diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap
index 13d0dce34d97e..9a690aa41ef4a 100644
--- a/libcxx/include/module.modulemap
+++ b/libcxx/include/module.modulemap
@@ -1401,6 +1401,7 @@ module std_private_iterator_concepts                [system] {
   export std_private_type_traits_is_reference
   export std_private_type_traits_remove_cvref
 }
+module std_private_iterator_const_iterator          [system] { header "__iterator/const_iterator.h" }
 module std_private_iterator_counted_iterator        [system] { header "__iterator/counted_iterator.h" }
 module std_private_iterator_cpp17_iterator_concepts [system] { header "__iterator/cpp17_iterator_concepts.h" }
 module std_private_iterator_data                    [system] { header "__iterator/data.h" }
@@ -1702,6 +1703,7 @@ module std_private_ranges_all                        [system] {
 module std_private_ranges_as_rvalue_view             [system] { header "__ranges/as_rvalue_view.h" }
 module std_private_ranges_chunk_by_view              [system] { header "__ranges/chunk_by_view.h" }
 module std_private_ranges_common_view                [system] { header "__ranges/common_view.h" }
+module std_private_ranges_const_access               [system] { header "__iterator/const_access.h" }
 module std_private_ranges_concepts                   [system] {
   header "__ranges/concepts.h"
   export std_private_iterator_concepts
diff --git a/libcxx/modules/std/iterator.inc b/libcxx/modules/std/iterator.inc
index 10c63d74e6e05..5278ceaf2785b 100644
--- a/libcxx/modules/std/iterator.inc
+++ b/libcxx/modules/std/iterator.inc
@@ -182,18 +182,21 @@ export namespace std {
   using std::insert_iterator;
   using std::inserter;
 
+#if _LIBCPP_STD_VER >= 23
   // [const.iterators], constant iterators and sentinels
   // [const.iterators.alias], alias templates
-  //  using std::const_iterator;
-  //  using std::const_sentinel;
-  //  using std::iter_const_reference_t;
+  using std::const_iterator;
+  using std::const_sentinel;
+  using std::iter_const_reference_t;
 
   // [const.iterators.iterator], class template basic_const_iterator
-  //  using std::basic_const_iterator;
+  using std::basic_const_iterator;
 
-  // using std::common_type;
+  using std::common_type;
 
-  //  using std::make_const_iterator;
+  using std::make_const_iterator;
+  using std::make_const_sentinel;
+#endif
 
   // [move.iterators], move iterators and sentinels
   using std::move_iterator;
diff --git a/libcxx/test/std/ranges/range.access/begin.pass.cpp b/libcxx/test/std/ranges/range.access/begin.pass.cpp
index 5ca3d59abb140..077d17c085b99 100644
--- a/libcxx/test/std/ranges/range.access/begin.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/begin.pass.cpp
@@ -9,7 +9,7 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 
 // std::ranges::begin
-// std::ranges::cbegin
+// std::ranges::cbegin // until C++23
 
 #include <ranges>
 
diff --git a/libcxx/test/std/ranges/range.access/end.pass.cpp b/libcxx/test/std/ranges/range.access/end.pass.cpp
index 3e465b357e985..19534d0341857 100644
--- a/libcxx/test/std/ranges/range.access/end.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/end.pass.cpp
@@ -9,7 +9,7 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 
 // std::ranges::end
-// std::ranges::cend
+// std::ranges::cend // until C++23
 
 #include <ranges>
 
diff --git a/libcxx/test/std/ranges/range.access/rbegin.pass.cpp b/libcxx/test/std/ranges/range.access/rbegin.pass.cpp
index 3997f38efd029..d34ee9f2da0a7 100644
--- a/libcxx/test/std/ranges/range.access/rbegin.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/rbegin.pass.cpp
@@ -9,7 +9,7 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 
 // std::ranges::rbegin
-// std::ranges::crbegin
+// std::ranges::crbegin // until C++23
 
 #include <ranges>
 
@@ -19,7 +19,9 @@
 #include "test_iterators.h"
 
 using RangeRBeginT = decltype(std::ranges::rbegin);
+#if _LIBCPP_STD_VER <= 23
 using RangeCRBeginT = decltype(std::ranges::crbegin);
+#endif // _LIBCPP_STD_VER <= 23
 
 static int globalBuff[8];
 
@@ -27,34 +29,42 @@ 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 (&)[]>);
+#if _LIBCPP_STD_VER <= 23
 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 (&)[]>);
+#endif // _LIBCPP_STD_VER <= 23
 
 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, const Incomplete (&&)[]>);
 static_assert(!std::is_invocable_v<RangeRBeginT, Incomplete(&&)[10]>);
 static_assert(!std::is_invocable_v<RangeRBeginT, const Incomplete(&&)[10]>);
+
+#if _LIBCPP_STD_VER <= 23
+static_assert(!std::is_invocable_v<RangeCRBeginT, Incomplete (&&)[]>);
+static_assert(!std::is_invocable_v<RangeCRBeginT, const Incomplete (&&)[]>);
 static_assert(!std::is_invocable_v<RangeCRBeginT, Incomplete(&&)[10]>);
 static_assert(!std::is_invocable_v<RangeCRBeginT, const Incomplete(&&)[10]>);
+#endif // _LIBCPP_STD_VER <= 23
 
 // 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(&)[]>);
+#if _LIBCPP_STD_VER <= 23
 LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeCRBeginT, Incomplete(&)[]>);
 LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeCRBeginT, const Incomplete(&)[]>);
+#endif // _LIBCPP_STD_VER <= 23
 
 // 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]>);
+#if _LIBCPP_STD_VER <= 23
 LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeCRBeginT, Incomplete(&)[10]>);
 LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeCRBeginT, const Incomplete(&)[10]>);
+#endif // _LIBCPP_STD_VER <= 23
 
 struct RBeginMember {
   int x;
@@ -66,45 +76,48 @@ 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&&>);
+#if _LIBCPP_STD_VER <= 23
 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&&>);
+#endif // _LIBCPP_STD_VER <= 23
 
 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*);
-  }
+  int* a[2];
+  int b[2][2];
+  struct Different {
+    char*& rbegin();
+    short*& rbegin() const;
+  } c;
+
+  ASSERT_SAME_TYPE(decltype(std::ranges::rbegin(x)), std::reverse_iterator<int**>);
+  ASSERT_SAME_TYPE(decltype(std::ranges::rbegin(x)), std::reverse_iterator<int(*)[2]>);
+  ASSERT_SAME_TYPE(decltype(std::ranges::rbegin(x)), char*);
+
+#if _LIBCPP_STD_VER <= 23
+  ASSERT_SAME_TYPE(decltype(std::ranges::crbegin(x)), std::reverse_iterator<int* const*>);
+  ASSERT_SAME_TYPE(decltype(std::ranges::crbegin(x)), std::reverse_iterator<const int(*)[2]>);
+  ASSERT_SAME_TYPE(decltype(std::ranges::crbegin(x)), short*);
+#endif // _LIBCPP_STD_VER <= 23
+
   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(a).base() == a + 2);
+  assert(std::ranges::rbegin(b).base() == b + 2);
   assert(std::ranges::rbegin(c).base() == c + 2);
+
+#if _LIBCPP_STD_VER <= 23
+  assert(std::ranges::crbegin(a).base() == a + 2);
+  assert(std::ranges::crbegin(b).base() == b + 2);
   assert(std::ranges::crbegin(c).base() == c + 2);
+#endif // _LIBCPP_STD_VER <= 23
 
   return true;
 }
@@ -131,8 +144,10 @@ struct NonConstRBeginMember {
 };
 static_assert( std::is_invocable_v<RangeRBeginT,  NonConstRBeginMember &>);
 static_assert(!std::is_invocable_v<RangeRBeginT,  NonConstRBeginMember const&>);
+#if _LIBCPP_STD_VER <= 23
 static_assert(!std::is_invocable_v<RangeCRBeginT, NonConstRBeginMember &>);
 static_assert(!std::is_invocable_v<RangeCRBeginT, NonConstRBeginMember const&>);
+#endif // _LIBCPP_STD_VER <= 23
 
 struct EnabledBorrowingRBeginMember {
   constexpr int *rbegin() const { return globalBuff; }
@@ -154,28 +169,28 @@ struct EmptyPtrRBeginMember {
 
 constexpr bool testRBeginMember() {
   RBeginMember a;
+  NonConstRBeginMember b;
+  EnabledBorrowingRBeginMember c;
+  RBeginMemberFunction d;
+  EmptyPtrRBeginMember e;
+
   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);
+
+#if _LIBCPP_STD_VER <= 23
+  assert(std::ranges::crbegin(a) == &a.x);
+  static_assert(!std::is_invocable_v<RangeCRBeginT, RBeginMember&&>);
+  static_assert(!std::is_invocable_v<RangeCRBeginT, NonConstRBeginMember&>);
+  assert(std::ranges::crbegin(c) == globalBuff);
+  assert(std::ranges::crbegin(std::move(c)) == globalBuff);
+  assert(std::ranges::crbegin(d) == &d.x);
   assert(std::ranges::crbegin(e) == &e.x);
+#endif // _LIBCPP_STD_VER <= 23
 
   return true;
 }
@@ -189,8 +204,10 @@ 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&>); // Ill-formed before P2602R2 Poison Pills are Too Toxic
+#if _LIBCPP_STD_VER <= 23
 static_assert( std::is_invocable_v<RangeCRBeginT, RBeginFunction const&>);
 static_assert( std::is_invocable_v<RangeCRBeginT, RBeginFunction &>);
+#endif // _LIBCPP_STD_VER <= 23
 
 struct RBeginFunctionReturnsInt {
   friend int rbegin(RBeginFunctionReturnsInt const&);
@@ -214,10 +231,12 @@ struct RBeginFunctionReturnsPtrConvertible {
 };
 static_assert(!std::is_invocable_v<RangeRBeginT, RBeginFunctionReturnsPtrConvertible const&>);
 
+#if _LIBCPP_STD_VER <= 23
 struct RBeginFunctionByValue {
   friend constexpr int *rbegin(RBeginFunctionByValue) { return globalBuff + 1; }
 };
 static_assert(!std::is_invocable_v<RangeCRBeginT, RBeginFunctionByValue>);
+#endif // _LIBCPP_STD_VER <= 23
 
 struct RBeginFunctionEnabledBorrowing {
   friend constexpr int *rbegin(RBeginFunctionEnabledBorrowing) { return globalBuff + 2; }
@@ -247,45 +266,44 @@ struct RBeginFunctionWithPrivateBeginMember {
 constexpr bool testRBeginFunction() {
   RBeginFunction a{};
   const RBeginFunction aa{};
-  assert(std::ranges::rbegin(a) == &a.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic
-  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{};
-  assert(std::ranges::rbegin(d) == &d.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic
-  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{};
+  RBeginFunctionWithPrivateBeginMember f{};
+  const RBeginFunctionWithPrivateBeginMember ff{};
+
+  assert(std::ranges::rbegin(a) == &a.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic
+  assert(std::ranges::rbegin(aa) == &aa.x);
+  assert(std::ranges::rbegin(b) == globalBuff + 1);
+  assert(std::ranges::rbegin(bb) == globalBuff + 1);
+  assert(std::ranges::rbegin(std::move(c)) == globalBuff + 2);
+  assert(std::ranges::rbegin(std::move(cc)) == globalBuff + 2);
+  assert(std::ranges::rbegin(d) == &d.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic
+  assert(std::ranges::rbegin(dd) == &dd.x);
   assert(std::ranges::rbegin(e) == &e.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic
   assert(std::ranges::rbegin(ee) == &ee.x);
+  assert(std::ranges::rbegin(f) == &f.y); // Ill-formed before P2602R2 Poison Pills are Too Toxic
+  assert(std::ranges::rbegin(ff) == &ff.y);
+
+#if _LIBCPP_STD_VER <= 23
+  assert(std::ranges::crbegin(a) == &a.x);
+  assert(std::ranges::crbegin(aa) == &aa.x);
+  assert(std::ranges::crbegin(b) == globalBuff + 1);
+  assert(std::ranges::crbegin(bb) == globalBuff + 1);
+  assert(std::ranges::crbegin(std::move(c)) == globalBuff + 2);
+  assert(std::ranges::crbegin(std::move(cc)) == globalBuff + 2);
+  assert(std::ranges::crbegin(d) == &d.x);
+  assert(std::ranges::crbegin(dd) == &dd.x);
   assert(std::ranges::crbegin(e) == &e.x);
   assert(std::ranges::crbegin(ee) == &ee.x);
-
-  RBeginFunctionWithPrivateBeginMember f{};
-  const RBeginFunctionWithPrivateBeginMember ff{};
-  assert(std::ranges::rbegin(f) == &f.y); // Ill-formed before P2602R2 Poison Pills are Too Toxic
   assert(std::ranges::crbegin(f) == &f.y);
-  assert(std::ranges::rbegin(ff) == &ff.y);
   assert(std::ranges::crbegin(ff) == &ff.y);
+#endif // _LIBCPP_STD_VER <= 23
 
   return true;
 }
@@ -301,7 +319,9 @@ struct MemberBeginEnd {
 };
 static_assert( std::is_invocable_v<RangeRBeginT, MemberBeginEnd&>);
 static_assert( std::is_invocable_v<RangeRBeginT, MemberBeginEnd const&>);
+#if _LIBCPP_STD_VER <= 23
 static_assert( std::is_invocable_v<RangeCRBeginT, MemberBeginEnd const&>);
+#endif // _LIBCPP_STD_VER <= 23
 
 struct FunctionBeginEnd {
   int b, e;
@@ -319,7 +339,9 @@ struct FunctionBeginEnd {
 };
 static_assert( std::is_invocable_v<RangeRBeginT, FunctionBeginEnd&>);
 static_assert( std::is_invocable_v<RangeRBeginT, FunctionBeginEnd const&>);
+#if _LIBCPP_STD_VER <= 23
 static_assert( std::is_invocable_v<RangeCRBeginT, FunctionBeginEnd const&>);
+#endif // _LIBCPP_STD_VER <= 23
 
 struct MemberBeginFunctionEnd {
   int b, e;
@@ -335,7 +357,9 @@ struct MemberBeginFunctionEnd {
 };
 static_assert( std::is_invocable_v<RangeRBeginT, MemberBeginFunctionEnd&>);
 static_assert( std::is_invocable_v<RangeRBeginT, MemberBeginFunctionEnd const&>);
+#if _LIBCPP_STD_VER <= 23
 static_assert( std::is_invocable_v<RangeCRBeginT, MemberBeginFunctionEnd const&>);
+#endif // _LIBCPP_STD_VER <= 23
 
 struct FunctionBeginMemberEnd {
   int b, e;
@@ -351,59 +375,77 @@ struct FunctionBeginMemberEnd {
 };
 static_assert( std::is_invocable_v<RangeRBeginT, FunctionBeginMemberEnd&>);
 static_assert( std::is_invocable_v<RangeRBeginT, FunctionBeginMemberEnd const&>);
+#if _LIBCPP_STD_VER <= 23
 static_assert( std::is_invocable_v<RangeCRBeginT, FunctionBeginMemberEnd const&>);
+#endif // _LIBCPP_STD_VER <= 23
 
 struct MemberBeginEndDifferentTypes {
   bidirectional_iterator<int*> begin();
   bidirectional_iterator<const int*> end();
 };
 static_assert(!std::is_invocable_v<RangeRBeginT, MemberBeginEndDifferentTypes&>);
+#if _LIBCPP_STD_VER <= 23
 static_assert(!std::is_invocable_v<RangeCRBeginT, MemberBeginEndDifferentTypes&>);
+#endif // _LIBCPP_STD_VER <= 23
 
 struct FunctionBeginEndDifferentTypes {
   friend bidirectional_iterator<int*> begin(FunctionBeginEndDifferentTypes&);
   friend bidirectional_iterator<const int*> end(FunctionBeginEndDifferentTypes&);
 };
 static_assert(!std::is_invocable_v<RangeRBeginT, FunctionBeginEndDifferentTypes&>);
+#if _LIBCPP_STD_VER <= 23
 static_assert(!std::is_invocable_v<RangeCRBeginT, FunctionBeginEndDifferentTypes&>);
+#endif // _LIBCPP_STD_VER <= 23
 
 struct MemberBeginEndForwardIterators {
   forward_iterator<int*> begin();
   forward_iterator<int*> end();
 };
 static_assert(!std::is_invocable_v<RangeRBeginT, MemberBeginEndForwardIterators&>);
+#if _LIBCPP_STD_VER <= 23
 static_assert(!std::is_invocable_v<RangeCRBeginT, MemberBeginEndForwardIterators&>);
+#endif // _LIBCPP_STD_VER <= 23
 
 struct FunctionBeginEndForwardIterators {
   friend forward_iterator<int*> begin(FunctionBeginEndForwardIterators&);
   friend forward_iterator<int*> end(FunctionBeginEndForwardIterators&);
 };
 static_assert(!std::is_invocable_v<RangeRBeginT, FunctionBeginEndForwardIterators&>);
+#if _LIBCPP_STD_VER <= 23
 static_assert(!std::is_invocable_v<RangeCRBeginT, FunctionBeginEndForwardIterators&>);
+#endif // _LIBCPP_STD_VER <= 23
 
 struct MemberBeginOnly {
   bidirectional_iterator<int*> begin() const;
 };
 static_assert(!std::is_invocable_v<RangeRBeginT, MemberBeginOnly&>);
+#if _LIBCPP_STD_VER <= 23
 static_assert(!std::is_invocable_v<RangeCRBeginT, MemberBeginOnly&>);
+#endif // _LIBCPP_STD_VER <= 23
 
 struct FunctionBeginOnly {
   friend bidirectional_iterator<int*> begin(FunctionBeginOnly&);
 };
 static_assert(!std::is_invocable_v<RangeRBeginT, FunctionBeginOnly&>);
+#if _LIBCPP_STD_VER <= 23
 static_assert(!std::is_invocable_v<RangeCRBeginT, FunctionBeginOnly&>);
+#endif // _LIBCPP_STD_VER <= 23
 
 struct MemberEndOnly {
   bidirectional_iterator<int*> end() const;
 };
 static_assert(!std::is_invocable_v<RangeRBeginT, MemberEndOnly&>);
+#if _LIBCPP_STD_VER <= 23
 static_assert(!std::is_invocable_v<RangeCRBeginT, MemberEndOnly&>);
+#endif // _LIBCPP_STD_VER <= 23
 
 struct FunctionEndOnly {
   friend bidirectional_iterator<int*> end(FunctionEndOnly&);
 };
 static_assert(!std::is_invocable_v<RangeRBeginT, FunctionEndOnly&>);
+#if _LIBCPP_STD_VER <= 23
 static_assert(!std::is_invocable_v<RangeCRBeginT, FunctionEndOnly&>);
+#endif // _LIBCPP_STD_VER <= 23
 
 // Make sure there is no clash between the following cases:
 // - the case that handles classes defining member `rbegin` and `rend` functions;
@@ -414,93 +456,112 @@ struct MemberBeginAndRBegin {
   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::is_invocable_v<RangeRBeginT, MemberBeginAndRBegin&>);
 static_assert( std::same_as<std::invoke_result_t<RangeRBeginT, MemberBeginAndRBegin&>, int*>);
+#if _LIBCPP_STD_VER <= 23
+static_assert(std::is_invocable_v<RangeCRBeginT, MemberBeginAndRBegin&>);
 static_assert( std::same_as<std::invoke_result_t<RangeCRBeginT, MemberBeginAndRBegin&>, int*>);
+#endif // _LIBCPP_STD_VER <= 23
 
 constexpr bool testBeginEnd() {
   MemberBeginEnd a{};
   const MemberBeginEnd aa{};
-  assert(base(std::ranges::rbegin(a).base()) == &a.e);
-  assert(base(std::ranges::crbegin(a).base()) == &a.ce);
-  assert(base(std::ranges::rbegin(aa).base()) == &aa.ce);
-  assert(base(std::ranges::crbegin(aa).base()) == &aa.ce);
-
   FunctionBeginEnd b{};
   const FunctionBeginEnd bb{};
-  assert(base(std::ranges::rbegin(b).base()) == &b.e);
-  assert(base(std::ranges::crbegin(b).base()) == &b.ce);
-  assert(base(std::ranges::rbegin(bb).base()) == &bb.ce);
-  assert(base(std::ranges::crbegin(bb).base()) == &bb.ce);
-
   MemberBeginFunctionEnd c{};
   const MemberBeginFunctionEnd cc{};
-  assert(base(std::ranges::rbegin(c).base()) == &c.e);
-  assert(base(std::ranges::crbegin(c).base()) == &c.ce);
-  assert(base(std::ranges::rbegin(cc).base()) == &cc.ce);
-  assert(base(std::ranges::crbegin(cc).base()) == &cc.ce);
-
   FunctionBeginMemberEnd d{};
   const FunctionBeginMemberEnd dd{};
+
+  assert(base(std::ranges::rbegin(a).base()) == &a.e);
+  assert(base(std::ranges::rbegin(aa).base()) == &aa.ce);
+  assert(base(std::ranges::rbegin(b).base()) == &b.e);
+  assert(base(std::ranges::rbegin(bb).base()) == &bb.ce);
+  assert(base(std::ranges::rbegin(c).base()) == &c.e);
+  assert(base(std::ranges::rbegin(cc).base()) == &cc.ce);
   assert(base(std::ranges::rbegin(d).base()) == &d.e);
-  assert(base(std::ranges::crbegin(d).base()) == &d.ce);
   assert(base(std::ranges::rbegin(dd).base()) == &dd.ce);
+
+#if _LIBCPP_STD_VER <= 23
+  assert(base(std::ranges::crbegin(a).base()) == &a.ce);
+  assert(base(std::ranges::crbegin(aa).base()) == &aa.ce);
+  assert(base(std::ranges::crbegin(b).base()) == &b.ce);
+  assert(base(std::ranges::crbegin(bb).base()) == &bb.ce);
+  assert(base(std::ranges::crbegin(c).base()) == &c.ce);
+  assert(base(std::ranges::crbegin(cc).base()) == &cc.ce);
+  assert(base(std::ranges::crbegin(d).base()) == &d.ce);
   assert(base(std::ranges::crbegin(dd).base()) == &dd.ce);
+#endif // _LIBCPP_STD_VER <= 23
 
   return true;
 }
 
 
 ASSERT_NOEXCEPT(std::ranges::rbegin(std::declval<int (&)[10]>()));
+#if _LIBCPP_STD_VER <= 23
 ASSERT_NOEXCEPT(std::ranges::crbegin(std::declval<int (&)[10]>()));
+#endif // _LIBCPP_STD_VER <= 23
 
 struct NoThrowMemberRBegin {
   ThrowingIterator<int> rbegin() const noexcept; // auto(t.rbegin()) doesn't throw
 } ntmb;
 static_assert(noexcept(std::ranges::rbegin(ntmb)));
+#if _LIBCPP_STD_VER <= 23
 static_assert(noexcept(std::ranges::crbegin(ntmb)));
+#endif // _LIBCPP_STD_VER <= 23
 
 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)));
+#if _LIBCPP_STD_VER <= 23
 static_assert(noexcept(std::ranges::crbegin(ntab)));
+#endif // _LIBCPP_STD_VER <= 23
 
 struct NoThrowMemberRBeginReturnsRef {
   ThrowingIterator<int>& rbegin() const noexcept; // auto(t.rbegin()) may throw
 } ntmbrr;
 static_assert(!noexcept(std::ranges::rbegin(ntmbrr)));
+#if _LIBCPP_STD_VER <= 23
 static_assert(!noexcept(std::ranges::crbegin(ntmbrr)));
+#endif // _LIBCPP_STD_VER <= 23
 
 struct RBeginReturnsArrayRef {
     auto rbegin() const noexcept -> int(&)[10];
 } brar;
 static_assert(noexcept(std::ranges::rbegin(brar)));
+#if _LIBCPP_STD_VER <= 23
 static_assert(noexcept(std::ranges::crbegin(brar)));
+#endif // _LIBCPP_STD_VER <= 23
 
 struct NoThrowBeginThrowingEnd {
   int* begin() const noexcept;
   int* end() const;
 } ntbte;
 static_assert(!noexcept(std::ranges::rbegin(ntbte)));
+#if _LIBCPP_STD_VER <= 23
 static_assert(!noexcept(std::ranges::crbegin(ntbte)));
+#endif // _LIBCPP_STD_VER <= 23
 
 struct NoThrowEndThrowingBegin {
   int* begin() const;
   int* end() const noexcept;
 } ntetb;
 static_assert(noexcept(std::ranges::rbegin(ntetb)));
+#if _LIBCPP_STD_VER <= 23
 static_assert(noexcept(std::ranges::crbegin(ntetb)));
+#endif // _LIBCPP_STD_VER <= 23
 
 // 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>*&>);
+#if _LIBCPP_STD_VER <= 23
 static_assert(!std::is_invocable_v<RangeCRBeginT, Holder<Incomplete>*>);
 static_assert(!std::is_invocable_v<RangeCRBeginT, Holder<Incomplete>*&>);
+#endif // _LIBCPP_STD_VER <= 23
 
 int main(int, char**) {
   static_assert(testReturnTypes());
diff --git a/libcxx/test/std/ranges/range.access/rend.pass.cpp b/libcxx/test/std/ranges/range.access/rend.pass.cpp
index f5f59edf19393..55c2e084d25c3 100644
--- a/libcxx/test/std/ranges/range.access/rend.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/rend.pass.cpp
@@ -9,7 +9,7 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 
 // std::ranges::rend
-// std::ranges::crend
+// std::ranges::crend // before C++23
 
 #include <ranges>
 
@@ -19,7 +19,9 @@
 #include "test_iterators.h"
 
 using RangeREndT = decltype(std::ranges::rend);
+#if _LIBCPP_STD_VER <= 23
 using RangeCREndT = decltype(std::ranges::crend);
+#endif // _LIBCPP_STD_VER <= 23
 
 static int globalBuff[8];
 
@@ -27,16 +29,20 @@ 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]>);
+#if _LIBCPP_STD_VER <= 23
 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]>);
+#endif // _LIBCPP_STD_VER <= 23
 
 struct Incomplete;
 static_assert(!std::is_invocable_v<RangeREndT, Incomplete(&&)[]>);
 static_assert(!std::is_invocable_v<RangeREndT, Incomplete(&&)[42]>);
+#if _LIBCPP_STD_VER <= 23
 static_assert(!std::is_invocable_v<RangeCREndT, Incomplete(&&)[]>);
 static_assert(!std::is_invocable_v<RangeCREndT, Incomplete(&&)[42]>);
+#endif // _LIBCPP_STD_VER <= 23
 
 struct REndMember {
   int x;
@@ -49,22 +55,28 @@ 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&&>);
+#if _LIBCPP_STD_VER <= 23
 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&&>);
+#endif // _LIBCPP_STD_VER <= 23
 
 constexpr bool testReturnTypes() {
   {
     int *x[2];
     ASSERT_SAME_TYPE(decltype(std::ranges::rend(x)), std::reverse_iterator<int**>);
+#if _LIBCPP_STD_VER <= 23
     ASSERT_SAME_TYPE(decltype(std::ranges::crend(x)), std::reverse_iterator<int* const*>);
+#endif // _LIBCPP_STD_VER <= 23
   }
 
   {
     int x[2][2];
     ASSERT_SAME_TYPE(decltype(std::ranges::rend(x)), std::reverse_iterator<int(*)[2]>);
+#if _LIBCPP_STD_VER <= 23
     ASSERT_SAME_TYPE(decltype(std::ranges::crend(x)), std::reverse_iterator<const int(*)[2]>);
+#endif // _LIBCPP_STD_VER <= 23
   }
 
   {
@@ -75,7 +87,9 @@ constexpr bool testReturnTypes() {
       sentinel_wrapper<short*>& rend() const;
     } x;
     ASSERT_SAME_TYPE(decltype(std::ranges::rend(x)), sentinel_wrapper<char*>);
+#if _LIBCPP_STD_VER <= 23
     ASSERT_SAME_TYPE(decltype(std::ranges::crend(x)), sentinel_wrapper<short*>);
+#endif // _LIBCPP_STD_VER <= 23
   }
 
   return true;
@@ -83,17 +97,19 @@ constexpr bool testReturnTypes() {
 
 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(a).base() == a);
+  assert(std::ranges::rend(b).base() == b);
   assert(std::ranges::rend(c).base() == c);
-  assert(std::ranges::crend(c).base() == c);
 
+#if _LIBCPP_STD_VER <= 23
+  assert(std::ranges::crend(b).base() == b);
+  assert(std::ranges::crend(a).base() == a);
+  assert(std::ranges::crend(c).base() == c);
+#endif // _LIBCPP_STD_VER <= 23                                                                                        \
+       //
   return true;
 }
 
@@ -130,8 +146,10 @@ struct NonConstREndMember {
 };
 static_assert( std::is_invocable_v<RangeREndT,  NonConstREndMember &>);
 static_assert(!std::is_invocable_v<RangeREndT,  NonConstREndMember const&>);
+#if _LIBCPP_STD_VER <= 23
 static_assert(!std::is_invocable_v<RangeCREndT, NonConstREndMember &>);
 static_assert(!std::is_invocable_v<RangeCREndT, NonConstREndMember const&>);
+#endif // _LIBCPP_STD_VER <= 23
 
 struct EnabledBorrowingREndMember {
   constexpr int* rbegin() const { return nullptr; }
@@ -163,24 +181,24 @@ struct EmptyPtrREndMember {
 
 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(a) == &a.x);
+  assert(std::ranges::rend(b) == &b.x);
+  assert(std::ranges::rend(std::move(c)) == &globalBuff[0]);
+  assert(std::ranges::rend(d) == &d.x);
   assert(std::ranges::rend(e) == &e.x);
+
+#if _LIBCPP_STD_VER <= 23
+  assert(std::ranges::crend(a) == &a.x);
+  static_assert(!std::is_invocable_v<RangeCREndT, decltype((b))>);
+  assert(std::ranges::crend(std::move(c)) == &globalBuff[0]);
+  assert(std::ranges::crend(d) == &d.x);
   assert(std::ranges::crend(e) == &e.x);
+#endif // _LIBCPP_STD_VER <= 23
 
   return true;
 }
@@ -197,8 +215,10 @@ 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&>); // Ill-formed before P2602R2 Poison Pills are Too Toxic
+#if _LIBCPP_STD_VER <= 23
 static_assert( std::is_invocable_v<RangeCREndT, REndFunction const&>);
 static_assert( std::is_invocable_v<RangeCREndT, REndFunction &>);
+#endif // _LIBCPP_STD_VER <= 23
 
 struct REndFunctionReturnsInt {
   friend constexpr int rbegin(REndFunctionReturnsInt const&);
@@ -233,7 +253,9 @@ struct REndFunctionByValue {
   friend constexpr int* rbegin(REndFunctionByValue) { return nullptr; }
   friend constexpr int* rend(REndFunctionByValue) { return &globalBuff[1]; }
 };
+#if _LIBCPP_STD_VER <= 23
 static_assert(!std::is_invocable_v<RangeCREndT, REndFunctionByValue>);
+#endif // _LIBCPP_STD_VER <= 23
 
 struct REndFunctionEnabledBorrowing {
   friend constexpr int* rbegin(REndFunctionEnabledBorrowing) { return nullptr; }
@@ -269,47 +291,45 @@ struct RBeginMemberEndFunction {
 
 constexpr bool testREndFunction() {
   const REndFunction a{};
-  assert(std::ranges::rend(a) == &a.x);
-  assert(std::ranges::crend(a) == &a.x);
   REndFunction aa{};
-  assert(std::ranges::rend(aa) == &aa.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic
-  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{};
-  assert(std::ranges::rend(dd) == &dd.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic
-  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{};
-  assert(std::ranges::rend(ee) == &ee.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic
-  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{};
-  assert(std::ranges::rend(ff) == &ff.y); // Ill-formed before P2602R2 Poison Pills are Too Toxic
-  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{};
+
+  assert(std::ranges::rend(a) == &a.x);
+  assert(std::ranges::rend(aa) == &aa.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic
+  assert(std::ranges::rend(b) == &globalBuff[1]);
+  assert(std::ranges::rend(std::move(c)) == &globalBuff[2]);
+  assert(std::ranges::rend(d) == &d.x);
+  assert(std::ranges::rend(dd) == &dd.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic
+  assert(std::ranges::rend(e) == &e.x);
+  assert(std::ranges::rend(ee) == &ee.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic
+  assert(std::ranges::rend(f) == &f.y);
+  assert(std::ranges::rend(ff) == &ff.y); // Ill-formed before P2602R2 Poison Pills are Too Toxic
+  assert(std::ranges::rend(g) == &g.x);
   assert(std::ranges::rend(gg) == &gg.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic
+
+#if _LIBCPP_STD_VER <= 23
+  assert(std::ranges::crend(a) == &a.x);
+  assert(std::ranges::crend(aa) == &aa.x);
+  assert(std::ranges::crend(b) == &globalBuff[1]);
+  assert(std::ranges::crend(std::move(c)) == &globalBuff[2]);
+  assert(std::ranges::crend(d) == &d.x);
+  assert(std::ranges::crend(dd) == &dd.x);
+  assert(std::ranges::crend(e) == &e.x);
+  assert(std::ranges::crend(ee) == &ee.x);
+  assert(std::ranges::crend(f) == &f.y);
+  assert(std::ranges::crend(ff) == &ff.y);
+  assert(std::ranges::crend(g) == &g.x);
   assert(std::ranges::crend(gg) == &gg.x);
+#endif // _LIBCPP_STD_VER <= 23
 
   return true;
 }
@@ -325,7 +345,9 @@ struct MemberBeginEnd {
 };
 static_assert( std::is_invocable_v<RangeREndT, MemberBeginEnd&>);
 static_assert( std::is_invocable_v<RangeREndT, MemberBeginEnd const&>);
+#if _LIBCPP_STD_VER <= 23
 static_assert( std::is_invocable_v<RangeCREndT, MemberBeginEnd const&>);
+#endif // _LIBCPP_STD_VER <= 23
 
 struct FunctionBeginEnd {
   int b, e;
@@ -343,7 +365,9 @@ struct FunctionBeginEnd {
 };
 static_assert( std::is_invocable_v<RangeREndT, FunctionBeginEnd&>);
 static_assert( std::is_invocable_v<RangeREndT, FunctionBeginEnd const&>);
+#if _LIBCPP_STD_VER <= 23
 static_assert( std::is_invocable_v<RangeCREndT, FunctionBeginEnd const&>);
+#endif // _LIBCPP_STD_VER <= 23
 
 struct MemberBeginFunctionEnd {
   int b, e;
@@ -359,7 +383,9 @@ struct MemberBeginFunctionEnd {
 };
 static_assert( std::is_invocable_v<RangeREndT, MemberBeginFunctionEnd&>);
 static_assert( std::is_invocable_v<RangeREndT, MemberBeginFunctionEnd const&>);
+#if _LIBCPP_STD_VER <= 23
 static_assert( std::is_invocable_v<RangeCREndT, MemberBeginFunctionEnd const&>);
+#endif // _LIBCPP_STD_VER <= 23
 
 struct FunctionBeginMemberEnd {
   int b, e;
@@ -375,59 +401,77 @@ struct FunctionBeginMemberEnd {
 };
 static_assert( std::is_invocable_v<RangeREndT, FunctionBeginMemberEnd&>);
 static_assert( std::is_invocable_v<RangeREndT, FunctionBeginMemberEnd const&>);
+#if _LIBCPP_STD_VER <= 23
 static_assert( std::is_invocable_v<RangeCREndT, FunctionBeginMemberEnd const&>);
+#endif // _LIBCPP_STD_VER <= 23
 
 struct MemberBeginEndDifferentTypes {
   bidirectional_iterator<int*> begin();
   bidirectional_iterator<const int*> end();
 };
 static_assert(!std::is_invocable_v<RangeREndT, MemberBeginEndDifferentTypes&>);
+#if _LIBCPP_STD_VER <= 23
 static_assert(!std::is_invocable_v<RangeCREndT, MemberBeginEndDifferentTypes&>);
+#endif // _LIBCPP_STD_VER <= 23
 
 struct FunctionBeginEndDifferentTypes {
   friend bidirectional_iterator<int*> begin(FunctionBeginEndDifferentTypes&);
   friend bidirectional_iterator<const int*> end(FunctionBeginEndDifferentTypes&);
 };
 static_assert(!std::is_invocable_v<RangeREndT, FunctionBeginEndDifferentTypes&>);
+#if _LIBCPP_STD_VER <= 23
 static_assert(!std::is_invocable_v<RangeCREndT, FunctionBeginEndDifferentTypes&>);
+#endif // _LIBCPP_STD_VER <= 23
 
 struct MemberBeginEndForwardIterators {
   forward_iterator<int*> begin();
   forward_iterator<int*> end();
 };
 static_assert(!std::is_invocable_v<RangeREndT, MemberBeginEndForwardIterators&>);
+#if _LIBCPP_STD_VER <= 23
 static_assert(!std::is_invocable_v<RangeCREndT, MemberBeginEndForwardIterators&>);
+#endif // _LIBCPP_STD_VER <= 23
 
 struct FunctionBeginEndForwardIterators {
   friend forward_iterator<int*> begin(FunctionBeginEndForwardIterators&);
   friend forward_iterator<int*> end(FunctionBeginEndForwardIterators&);
 };
 static_assert(!std::is_invocable_v<RangeREndT, FunctionBeginEndForwardIterators&>);
+#if _LIBCPP_STD_VER <= 23
 static_assert(!std::is_invocable_v<RangeCREndT, FunctionBeginEndForwardIterators&>);
+#endif // _LIBCPP_STD_VER <= 23
 
 struct MemberBeginOnly {
   bidirectional_iterator<int*> begin() const;
 };
 static_assert(!std::is_invocable_v<RangeREndT, MemberBeginOnly&>);
+#if _LIBCPP_STD_VER <= 23
 static_assert(!std::is_invocable_v<RangeCREndT, MemberBeginOnly&>);
+#endif // _LIBCPP_STD_VER <= 23
 
 struct FunctionBeginOnly {
   friend bidirectional_iterator<int*> begin(FunctionBeginOnly&);
 };
 static_assert(!std::is_invocable_v<RangeREndT, FunctionBeginOnly&>);
+#if _LIBCPP_STD_VER <= 23
 static_assert(!std::is_invocable_v<RangeCREndT, FunctionBeginOnly&>);
+#endif // _LIBCPP_STD_VER <= 23
 
 struct MemberEndOnly {
   bidirectional_iterator<int*> end() const;
 };
 static_assert(!std::is_invocable_v<RangeREndT, MemberEndOnly&>);
+#if _LIBCPP_STD_VER <= 23
 static_assert(!std::is_invocable_v<RangeCREndT, MemberEndOnly&>);
+#endif // _LIBCPP_STD_VER <= 23
 
 struct FunctionEndOnly {
   friend bidirectional_iterator<int*> end(FunctionEndOnly&);
 };
 static_assert(!std::is_invocable_v<RangeREndT, FunctionEndOnly&>);
+#if _LIBCPP_STD_VER <= 23
 static_assert(!std::is_invocable_v<RangeCREndT, FunctionEndOnly&>);
+#endif // _LIBCPP_STD_VER <= 23
 
 // Make sure there is no clash between the following cases:
 // - the case that handles classes defining member `rbegin` and `rend` functions;
@@ -438,53 +482,60 @@ struct MemberBeginAndRBegin {
   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::is_invocable_v<RangeREndT, MemberBeginAndRBegin&>);
 static_assert( std::same_as<std::invoke_result_t<RangeREndT, MemberBeginAndRBegin&>, int*>);
+#if _LIBCPP_STD_VER <= 23
+static_assert(std::is_invocable_v<RangeCREndT, MemberBeginAndRBegin&>);
 static_assert( std::same_as<std::invoke_result_t<RangeCREndT, MemberBeginAndRBegin&>, int*>);
+#endif // _LIBCPP_STD_VER <= 23
 
 constexpr bool testBeginEnd() {
   MemberBeginEnd a{};
   const MemberBeginEnd aa{};
-  assert(base(std::ranges::rend(a).base()) == &a.b);
-  assert(base(std::ranges::crend(a).base()) == &a.cb);
-  assert(base(std::ranges::rend(aa).base()) == &aa.cb);
-  assert(base(std::ranges::crend(aa).base()) == &aa.cb);
-
   FunctionBeginEnd b{};
   const FunctionBeginEnd bb{};
-  assert(base(std::ranges::rend(b).base()) == &b.b);
-  assert(base(std::ranges::crend(b).base()) == &b.cb);
-  assert(base(std::ranges::rend(bb).base()) == &bb.cb);
-  assert(base(std::ranges::crend(bb).base()) == &bb.cb);
-
   MemberBeginFunctionEnd c{};
   const MemberBeginFunctionEnd cc{};
-  assert(base(std::ranges::rend(c).base()) == &c.b);
-  assert(base(std::ranges::crend(c).base()) == &c.cb);
-  assert(base(std::ranges::rend(cc).base()) == &cc.cb);
-  assert(base(std::ranges::crend(cc).base()) == &cc.cb);
-
   FunctionBeginMemberEnd d{};
   const FunctionBeginMemberEnd dd{};
+
+  assert(base(std::ranges::rend(a).base()) == &a.b);
+  assert(base(std::ranges::rend(aa).base()) == &aa.cb);
+  assert(base(std::ranges::rend(b).base()) == &b.b);
+  assert(base(std::ranges::rend(bb).base()) == &bb.cb);
+  assert(base(std::ranges::rend(c).base()) == &c.b);
+  assert(base(std::ranges::rend(cc).base()) == &cc.cb);
   assert(base(std::ranges::rend(d).base()) == &d.b);
-  assert(base(std::ranges::crend(d).base()) == &d.cb);
   assert(base(std::ranges::rend(dd).base()) == &dd.cb);
+
+#if _LIBCPP_STD_VER <= 23
+  assert(base(std::ranges::crend(a).base()) == &a.cb);
+  assert(base(std::ranges::crend(aa).base()) == &aa.cb);
+  assert(base(std::ranges::crend(b).base()) == &b.cb);
+  assert(base(std::ranges::crend(bb).base()) == &bb.cb);
+  assert(base(std::ranges::crend(c).base()) == &c.cb);
+  assert(base(std::ranges::crend(cc).base()) == &cc.cb);
+  assert(base(std::ranges::crend(d).base()) == &d.cb);
   assert(base(std::ranges::crend(dd).base()) == &dd.cb);
+#endif // _LIBCPP_STD_VER <= 23
 
   return true;
 }
 
 
 ASSERT_NOEXCEPT(std::ranges::rend(std::declval<int (&)[10]>()));
+#if _LIBCPP_STD_VER <= 23
 ASSERT_NOEXCEPT(std::ranges::crend(std::declval<int (&)[10]>()));
+#endif // _LIBCPP_STD_VER <= 23
 
 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)));
+#if _LIBCPP_STD_VER <= 23
 static_assert(noexcept(std::ranges::crend(ntmre)));
+#endif // _LIBCPP_STD_VER <= 23
 
 struct NoThrowADLREnd {
   ThrowingIterator<int> rbegin() const;
@@ -492,43 +543,55 @@ struct NoThrowADLREnd {
   friend ThrowingIterator<int> rend(const NoThrowADLREnd&) noexcept;
 } ntare;
 static_assert(noexcept(std::ranges::rend(ntare)));
+#if _LIBCPP_STD_VER <= 23
 static_assert(noexcept(std::ranges::crend(ntare)));
+#endif // _LIBCPP_STD_VER <= 23
 
 struct NoThrowMemberREndReturnsRef {
   ThrowingIterator<int> rbegin() const;
   ThrowingIterator<int>& rend() const noexcept; // auto(t.rend()) may throw
 } ntmrerr;
 static_assert(!noexcept(std::ranges::rend(ntmrerr)));
+#if _LIBCPP_STD_VER <= 23
 static_assert(!noexcept(std::ranges::crend(ntmrerr)));
+#endif // _LIBCPP_STD_VER <= 23
 
 struct REndReturnsArrayRef {
     auto rbegin() const noexcept -> int(&)[10];
     auto rend() const noexcept -> int(&)[10];
 } rerar;
 static_assert(noexcept(std::ranges::rend(rerar)));
+#if _LIBCPP_STD_VER <= 23
 static_assert(noexcept(std::ranges::crend(rerar)));
+#endif // _LIBCPP_STD_VER <= 23
 
 struct NoThrowBeginThrowingEnd {
   int* begin() const noexcept;
   int* end() const;
 } ntbte;
 static_assert(noexcept(std::ranges::rend(ntbte)));
+#if _LIBCPP_STD_VER <= 23
 static_assert(noexcept(std::ranges::crend(ntbte)));
+#endif // _LIBCPP_STD_VER <= 23
 
 struct NoThrowEndThrowingBegin {
   int* begin() const;
   int* end() const noexcept;
 } ntetb;
 static_assert(!noexcept(std::ranges::rend(ntetb)));
+#if _LIBCPP_STD_VER <= 23
 static_assert(!noexcept(std::ranges::crend(ntetb)));
+#endif // _LIBCPP_STD_VER <= 23
 
 // 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>*&>);
+#if _LIBCPP_STD_VER <= 23
 static_assert(!std::is_invocable_v<RangeCREndT, Holder<Incomplete>*>);
 static_assert(!std::is_invocable_v<RangeCREndT, Holder<Incomplete>*&>);
+#endif // _LIBCPP_STD_VER <= 23
 
 int main(int, char**) {
   static_assert(testReturnTypes());

>From 9fae0d6d940986bfecdf0edd0c6ba1ce79cbbc8c Mon Sep 17 00:00:00 2001
From: Nicole Mazzuca <nicole at strega-nil.co>
Date: Wed, 24 Jul 2024 18:46:33 +0200
Subject: [PATCH 3/4] fix all the failing tests

still need to add tests of my own, but now ready to be reviewed at least
---
 libcxx/include/__ranges/const_access.h        |   2 +
 libcxx/include/module.modulemap               |   2 +-
 .../std/ranges/range.access/begin.pass.cpp    | 158 +++++++++-------
 .../test/std/ranges/range.access/end.pass.cpp | 157 +++++++++-------
 .../std/ranges/range.access/rbegin.pass.cpp   | 152 +++++++--------
 .../std/ranges/range.access/rend.pass.cpp     | 174 +++++++++---------
 6 files changed, 347 insertions(+), 298 deletions(-)

diff --git a/libcxx/include/__ranges/const_access.h b/libcxx/include/__ranges/const_access.h
index 270bf7b2f1675..df2811839bc46 100644
--- a/libcxx/include/__ranges/const_access.h
+++ b/libcxx/include/__ranges/const_access.h
@@ -17,7 +17,9 @@
 #include <__ranges/rbegin.h>
 #include <__ranges/rend.h>
 #include <__type_traits/is_reference.h>
+#include <__type_traits/remove_cv.h>
 #include <__type_traits/remove_reference.h>
+#include <__utility/declval.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap
index 9a690aa41ef4a..852c5b8c9fab5 100644
--- a/libcxx/include/module.modulemap
+++ b/libcxx/include/module.modulemap
@@ -1703,7 +1703,7 @@ module std_private_ranges_all                        [system] {
 module std_private_ranges_as_rvalue_view             [system] { header "__ranges/as_rvalue_view.h" }
 module std_private_ranges_chunk_by_view              [system] { header "__ranges/chunk_by_view.h" }
 module std_private_ranges_common_view                [system] { header "__ranges/common_view.h" }
-module std_private_ranges_const_access               [system] { header "__iterator/const_access.h" }
+module std_private_ranges_const_access               [system] { header "__ranges/const_access.h" }
 module std_private_ranges_concepts                   [system] {
   header "__ranges/concepts.h"
   export std_private_iterator_concepts
diff --git a/libcxx/test/std/ranges/range.access/begin.pass.cpp b/libcxx/test/std/ranges/range.access/begin.pass.cpp
index 077d17c085b99..b0097dff17103 100644
--- a/libcxx/test/std/ranges/range.access/begin.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/begin.pass.cpp
@@ -19,7 +19,9 @@
 #include "test_iterators.h"
 
 using RangeBeginT = decltype(std::ranges::begin);
+#if _LIBCPP_STD_VER < 23
 using RangeCBeginT = decltype(std::ranges::cbegin);
+#endif // _LIBCPP_STD_VER < 23
 
 static int globalBuff[8];
 
@@ -27,33 +29,43 @@ static_assert(!std::is_invocable_v<RangeBeginT, int (&&)[10]>);
 static_assert( std::is_invocable_v<RangeBeginT, int (&)[10]>);
 static_assert(!std::is_invocable_v<RangeBeginT, int (&&)[]>);
 static_assert( std::is_invocable_v<RangeBeginT, int (&)[]>);
+#if _LIBCPP_STD_VER < 23
 static_assert(!std::is_invocable_v<RangeCBeginT, int (&&)[10]>);
 static_assert( std::is_invocable_v<RangeCBeginT, int (&)[10]>);
 static_assert(!std::is_invocable_v<RangeCBeginT, int (&&)[]>);
 static_assert( std::is_invocable_v<RangeCBeginT, int (&)[]>);
+#endif // _LIBCPP_STD_VER < 23
 
 struct Incomplete;
 static_assert(!std::is_invocable_v<RangeBeginT, Incomplete(&&)[]>);
 static_assert(!std::is_invocable_v<RangeBeginT, const Incomplete(&&)[]>);
+#if _LIBCPP_STD_VER < 23
 static_assert(!std::is_invocable_v<RangeCBeginT, Incomplete(&&)[]>);
 static_assert(!std::is_invocable_v<RangeCBeginT, const Incomplete(&&)[]>);
+#endif // _LIBCPP_STD_VER < 23
 
 static_assert(!std::is_invocable_v<RangeBeginT, Incomplete(&&)[10]>);
 static_assert(!std::is_invocable_v<RangeBeginT, const Incomplete(&&)[10]>);
+#if _LIBCPP_STD_VER < 23
 static_assert(!std::is_invocable_v<RangeCBeginT, Incomplete(&&)[10]>);
 static_assert(!std::is_invocable_v<RangeCBeginT, const Incomplete(&&)[10]>);
+#endif // _LIBCPP_STD_VER < 23
 
 // This case is IFNDR; we handle it SFINAE-friendly.
 LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeBeginT, Incomplete(&)[]>);
 LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeBeginT, const Incomplete(&)[]>);
+#if _LIBCPP_STD_VER < 23
 LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeCBeginT, Incomplete(&)[]>);
 LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeCBeginT, const Incomplete(&)[]>);
+#endif // _LIBCPP_STD_VER < 23
 
 // This case is IFNDR; we handle it SFINAE-friendly.
 LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeBeginT, Incomplete(&)[10]>);
 LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeBeginT, const Incomplete(&)[10]>);
+#if _LIBCPP_STD_VER < 23
 LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeCBeginT, Incomplete(&)[10]>);
 LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeCBeginT, const Incomplete(&)[10]>);
+#endif // _LIBCPP_STD_VER < 23
 
 struct BeginMember {
   int x;
@@ -65,45 +77,48 @@ static_assert( std::is_invocable_v<RangeBeginT, BeginMember &>);
 static_assert(!std::is_invocable_v<RangeBeginT, BeginMember &&>);
 static_assert( std::is_invocable_v<RangeBeginT, BeginMember const&>);
 static_assert(!std::is_invocable_v<RangeBeginT, BeginMember const&&>);
+#if _LIBCPP_STD_VER < 23
 static_assert( std::is_invocable_v<RangeCBeginT, BeginMember &>);
 static_assert(!std::is_invocable_v<RangeCBeginT, BeginMember &&>);
 static_assert( std::is_invocable_v<RangeCBeginT, BeginMember const&>);
 static_assert(!std::is_invocable_v<RangeCBeginT, BeginMember const&&>);
+#endif // _LIBCPP_STD_VER < 23
 
 constexpr bool testReturnTypes() {
-  {
-    int *x[2];
-    ASSERT_SAME_TYPE(decltype(std::ranges::begin(x)), int**);
-    ASSERT_SAME_TYPE(decltype(std::ranges::cbegin(x)), int* const*);
-  }
-  {
-    int x[2][2];
-    ASSERT_SAME_TYPE(decltype(std::ranges::begin(x)), int(*)[2]);
-    ASSERT_SAME_TYPE(decltype(std::ranges::cbegin(x)), const int(*)[2]);
-  }
-  {
-    struct Different {
-      char*& begin();
-      short*& begin() const;
-    } x;
-    ASSERT_SAME_TYPE(decltype(std::ranges::begin(x)), char*);
-    ASSERT_SAME_TYPE(decltype(std::ranges::cbegin(x)), short*);
-  }
+  int* a[2];
+  int b[2][2];
+  struct Different {
+    char*& begin();
+    short*& begin() const;
+  } c;
+
+  ASSERT_SAME_TYPE(decltype(std::ranges::begin(a)), int**);
+  ASSERT_SAME_TYPE(decltype(std::ranges::begin(b)), int(*)[2]);
+  ASSERT_SAME_TYPE(decltype(std::ranges::begin(c)), char*);
+
+#if _LIBCPP_STD_VER < 23
+  ASSERT_SAME_TYPE(decltype(std::ranges::cbegin(a)), int* const*);
+  ASSERT_SAME_TYPE(decltype(std::ranges::cbegin(b)), const int(*)[2]);
+  ASSERT_SAME_TYPE(decltype(std::ranges::cbegin(c)), short*);
+#endif // _LIBCPP_STD_VER < 23
+
   return true;
 }
 
 constexpr bool testArray() {
   int a[2];
-  assert(std::ranges::begin(a) == a);
-  assert(std::ranges::cbegin(a) == a);
-
   int b[2][2];
-  assert(std::ranges::begin(b) == b);
-  assert(std::ranges::cbegin(b) == b);
-
   BeginMember c[2];
+
+  assert(std::ranges::begin(a) == a);
+  assert(std::ranges::begin(b) == b);
   assert(std::ranges::begin(c) == c);
+
+#if _LIBCPP_STD_VER < 23
+  assert(std::ranges::cbegin(a) == a);
+  assert(std::ranges::cbegin(b) == b);
   assert(std::ranges::cbegin(c) == c);
+#endif // _LIBCPP_STD_VER < 23
 
   return true;
 }
@@ -136,8 +151,10 @@ struct NonConstBeginMember {
 };
 static_assert( std::is_invocable_v<RangeBeginT,  NonConstBeginMember &>);
 static_assert(!std::is_invocable_v<RangeBeginT,  NonConstBeginMember const&>);
+#if _LIBCPP_STD_VER < 23
 static_assert(!std::is_invocable_v<RangeCBeginT, NonConstBeginMember &>);
 static_assert(!std::is_invocable_v<RangeCBeginT, NonConstBeginMember const&>);
+#endif // _LIBCPP_STD_VER < 23
 
 struct EnabledBorrowingBeginMember {
   constexpr int *begin() const { return &globalBuff[0]; }
@@ -159,28 +176,28 @@ struct EmptyPtrBeginMember {
 
 constexpr bool testBeginMember() {
   BeginMember a;
+  NonConstBeginMember b;
+  EnabledBorrowingBeginMember c;
+  BeginMemberFunction d;
+  EmptyPtrBeginMember e;
+
   assert(std::ranges::begin(a) == &a.x);
-  assert(std::ranges::cbegin(a) == &a.x);
   static_assert(!std::is_invocable_v<RangeBeginT, BeginMember&&>);
-  static_assert(!std::is_invocable_v<RangeCBeginT, BeginMember&&>);
-
-  NonConstBeginMember b;
   assert(std::ranges::begin(b) == &b.x);
-  static_assert(!std::is_invocable_v<RangeCBeginT, NonConstBeginMember&>);
-
-  EnabledBorrowingBeginMember c;
   assert(std::ranges::begin(c) == &globalBuff[0]);
-  assert(std::ranges::cbegin(c) == &globalBuff[0]);
   assert(std::ranges::begin(std::move(c)) == &globalBuff[0]);
-  assert(std::ranges::cbegin(std::move(c)) == &globalBuff[0]);
-
-  BeginMemberFunction d;
   assert(std::ranges::begin(d) == &d.x);
-  assert(std::ranges::cbegin(d) == &d.x);
-
-  EmptyPtrBeginMember e;
   assert(std::ranges::begin(e) == &e.x);
+
+#if _LIBCPP_STD_VER < 23
+  assert(std::ranges::cbegin(a) == &a.x);
+  static_assert(!std::is_invocable_v<RangeCBeginT, BeginMember&&>);
+  static_assert(!std::is_invocable_v<RangeCBeginT, NonConstBeginMember&>);
+  assert(std::ranges::cbegin(c) == &globalBuff[0]);
+  assert(std::ranges::cbegin(std::move(c)) == &globalBuff[0]);
+  assert(std::ranges::cbegin(d) == &d.x);
   assert(std::ranges::cbegin(e) == &e.x);
+#endif // _LIBCPP_STD_VER < 23
 
   return true;
 }
@@ -193,8 +210,10 @@ struct BeginFunction {
 static_assert( std::is_invocable_v<RangeBeginT,  BeginFunction const&>);
 static_assert(!std::is_invocable_v<RangeBeginT,  BeginFunction &&>);
 static_assert(std::is_invocable_v<RangeBeginT, BeginFunction&>); // Ill-formed before P2602R2 Poison Pills are Too Toxic
+#if _LIBCPP_STD_VER < 23
 static_assert( std::is_invocable_v<RangeCBeginT, BeginFunction const&>);
 static_assert( std::is_invocable_v<RangeCBeginT, BeginFunction &>);
+#endif // _LIBCPP_STD_VER < 23
 
 struct BeginFunctionReturnsInt {
   friend int begin(BeginFunctionReturnsInt const&);
@@ -215,7 +234,9 @@ static_assert(!std::is_invocable_v<RangeBeginT, BeginFunctionReturnsPtrConvertib
 struct BeginFunctionByValue {
   friend constexpr int *begin(BeginFunctionByValue) { return &globalBuff[1]; }
 };
+#if _LIBCPP_STD_VER < 23
 static_assert(!std::is_invocable_v<RangeCBeginT, BeginFunctionByValue>);
+#endif // _LIBCPP_STD_VER < 23
 
 struct BeginFunctionEnabledBorrowing {
   friend constexpr int *begin(BeginFunctionEnabledBorrowing) { return &globalBuff[2]; }
@@ -245,85 +266,96 @@ struct BeginFunctionWithPrivateBeginMember {
 constexpr bool testBeginFunction() {
   BeginFunction a{};
   const BeginFunction aa{};
-  assert(std::ranges::begin(a) == &a.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic
-  assert(std::ranges::cbegin(a) == &a.x);
-  assert(std::ranges::begin(aa) == &aa.x);
-  assert(std::ranges::cbegin(aa) == &aa.x);
-
   BeginFunctionByValue b{};
   const BeginFunctionByValue bb{};
-  assert(std::ranges::begin(b) == &globalBuff[1]);
-  assert(std::ranges::cbegin(b) == &globalBuff[1]);
-  assert(std::ranges::begin(bb) == &globalBuff[1]);
-  assert(std::ranges::cbegin(bb) == &globalBuff[1]);
-
   BeginFunctionEnabledBorrowing c{};
   const BeginFunctionEnabledBorrowing cc{};
-  assert(std::ranges::begin(std::move(c)) == &globalBuff[2]);
-  assert(std::ranges::cbegin(std::move(c)) == &globalBuff[2]);
-  assert(std::ranges::begin(std::move(cc)) == &globalBuff[2]);
-  assert(std::ranges::cbegin(std::move(cc)) == &globalBuff[2]);
-
   BeginFunctionReturnsEmptyPtr d{};
   const BeginFunctionReturnsEmptyPtr dd{};
-  assert(std::ranges::begin(d) == &d.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic
-  assert(std::ranges::cbegin(d) == &d.x);
-  assert(std::ranges::begin(dd) == &dd.x);
-  assert(std::ranges::cbegin(dd) == &dd.x);
-
   BeginFunctionWithDataMember e{};
   const BeginFunctionWithDataMember ee{};
+  BeginFunctionWithPrivateBeginMember f{};
+  const BeginFunctionWithPrivateBeginMember ff{};
+
+  assert(std::ranges::begin(a) == &a.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic
+  assert(std::ranges::begin(aa) == &aa.x);
+  assert(std::ranges::begin(b) == &globalBuff[1]);
+  assert(std::ranges::begin(bb) == &globalBuff[1]);
+  assert(std::ranges::begin(std::move(c)) == &globalBuff[2]);
+  assert(std::ranges::begin(std::move(cc)) == &globalBuff[2]);
+  assert(std::ranges::begin(d) == &d.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic
+  assert(std::ranges::begin(dd) == &dd.x);
   assert(std::ranges::begin(e) == &e.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic
   assert(std::ranges::begin(ee) == &ee.x);
+  assert(std::ranges::begin(f) == &f.y); // Ill-formed before P2602R2 Poison Pills are Too Toxic
+  assert(std::ranges::begin(ff) == &ff.y);
+
+#if _LIBCPP_STD_VER < 23
+  assert(std::ranges::cbegin(a) == &a.x);
+  assert(std::ranges::cbegin(aa) == &aa.x);
+  assert(std::ranges::cbegin(b) == &globalBuff[1]);
+  assert(std::ranges::cbegin(bb) == &globalBuff[1]);
+  assert(std::ranges::cbegin(std::move(c)) == &globalBuff[2]);
+  assert(std::ranges::cbegin(std::move(cc)) == &globalBuff[2]);
+  assert(std::ranges::cbegin(d) == &d.x);
+  assert(std::ranges::cbegin(dd) == &dd.x);
   assert(std::ranges::cbegin(e) == &e.x);
   assert(std::ranges::cbegin(ee) == &ee.x);
-
-  BeginFunctionWithPrivateBeginMember f{};
-  const BeginFunctionWithPrivateBeginMember ff{};
-  assert(std::ranges::begin(f) == &f.y); // Ill-formed before P2602R2 Poison Pills are Too Toxic
   assert(std::ranges::cbegin(f) == &f.y);
-  assert(std::ranges::begin(ff) == &ff.y);
   assert(std::ranges::cbegin(ff) == &ff.y);
+#endif // _LIBCPP_STD_VER < 23
 
   return true;
 }
 
 
 ASSERT_NOEXCEPT(std::ranges::begin(std::declval<int (&)[10]>()));
+#if _LIBCPP_STD_VER < 23
 ASSERT_NOEXCEPT(std::ranges::cbegin(std::declval<int (&)[10]>()));
+#endif // _LIBCPP_STD_VER < 23
 
 struct NoThrowMemberBegin {
   ThrowingIterator<int> begin() const noexcept; // auto(t.begin()) doesn't throw
 } ntmb;
 static_assert(noexcept(std::ranges::begin(ntmb)));
+#if _LIBCPP_STD_VER < 23
 static_assert(noexcept(std::ranges::cbegin(ntmb)));
+#endif // _LIBCPP_STD_VER < 23
 
 struct NoThrowADLBegin {
   friend ThrowingIterator<int> begin(NoThrowADLBegin&) noexcept;  // auto(begin(t)) doesn't throw
   friend ThrowingIterator<int> begin(const NoThrowADLBegin&) noexcept;
 } ntab;
 static_assert(noexcept(std::ranges::begin(ntab)));
+#if _LIBCPP_STD_VER < 23
 static_assert(noexcept(std::ranges::cbegin(ntab)));
+#endif // _LIBCPP_STD_VER < 23
 
 struct NoThrowMemberBeginReturnsRef {
   ThrowingIterator<int>& begin() const noexcept; // auto(t.begin()) may throw
 } ntmbrr;
 static_assert(!noexcept(std::ranges::begin(ntmbrr)));
+#if _LIBCPP_STD_VER < 23
 static_assert(!noexcept(std::ranges::cbegin(ntmbrr)));
+#endif // _LIBCPP_STD_VER < 23
 
 struct BeginReturnsArrayRef {
     auto begin() const noexcept -> int(&)[10];
 } brar;
 static_assert(noexcept(std::ranges::begin(brar)));
+#if _LIBCPP_STD_VER < 23
 static_assert(noexcept(std::ranges::cbegin(brar)));
+#endif // _LIBCPP_STD_VER < 23
 
 // Test ADL-proofing.
 struct Incomplete;
 template<class T> struct Holder { T t; };
 static_assert(!std::is_invocable_v<RangeBeginT, Holder<Incomplete>*>);
 static_assert(!std::is_invocable_v<RangeBeginT, Holder<Incomplete>*&>);
+#if _LIBCPP_STD_VER < 23
 static_assert(!std::is_invocable_v<RangeCBeginT, Holder<Incomplete>*>);
 static_assert(!std::is_invocable_v<RangeCBeginT, Holder<Incomplete>*&>);
+#endif // _LIBCPP_STD_VER < 23
 
 int main(int, char**) {
   static_assert(testReturnTypes());
diff --git a/libcxx/test/std/ranges/range.access/end.pass.cpp b/libcxx/test/std/ranges/range.access/end.pass.cpp
index 19534d0341857..ffbc5ba3d16e4 100644
--- a/libcxx/test/std/ranges/range.access/end.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/end.pass.cpp
@@ -19,7 +19,9 @@
 #include "test_iterators.h"
 
 using RangeEndT = decltype(std::ranges::end);
+#if _LIBCPP_STD_VER < 23
 using RangeCEndT = decltype(std::ranges::cend);
+#endif // _LIBCPP_STD_VER < 23
 
 static int globalBuff[8];
 
@@ -27,16 +29,20 @@ static_assert(!std::is_invocable_v<RangeEndT, int (&&)[]>);
 static_assert(!std::is_invocable_v<RangeEndT, int (&)[]>);
 static_assert(!std::is_invocable_v<RangeEndT, int (&&)[10]>);
 static_assert( std::is_invocable_v<RangeEndT, int (&)[10]>);
+#if _LIBCPP_STD_VER < 23
 static_assert(!std::is_invocable_v<RangeCEndT, int (&&)[]>);
 static_assert(!std::is_invocable_v<RangeCEndT, int (&)[]>);
 static_assert(!std::is_invocable_v<RangeCEndT, int (&&)[10]>);
 static_assert( std::is_invocable_v<RangeCEndT, int (&)[10]>);
+#endif // _LIBCPP_STD_VER < 23
 
 struct Incomplete;
 static_assert(!std::is_invocable_v<RangeEndT, Incomplete(&&)[]>);
 static_assert(!std::is_invocable_v<RangeEndT, Incomplete(&&)[42]>);
+#if _LIBCPP_STD_VER < 23
 static_assert(!std::is_invocable_v<RangeCEndT, Incomplete(&&)[]>);
 static_assert(!std::is_invocable_v<RangeCEndT, Incomplete(&&)[42]>);
+#endif // _LIBCPP_STD_VER < 23
 
 struct EndMember {
   int x;
@@ -49,47 +55,50 @@ static_assert( std::is_invocable_v<RangeEndT, EndMember &>);
 static_assert(!std::is_invocable_v<RangeEndT, EndMember &&>);
 static_assert( std::is_invocable_v<RangeEndT, EndMember const&>);
 static_assert(!std::is_invocable_v<RangeEndT, EndMember const&&>);
+#if _LIBCPP_STD_VER < 23
 static_assert( std::is_invocable_v<RangeCEndT, EndMember &>);
 static_assert(!std::is_invocable_v<RangeCEndT, EndMember &&>);
 static_assert( std::is_invocable_v<RangeCEndT, EndMember const&>);
 static_assert(!std::is_invocable_v<RangeCEndT, EndMember const&&>);
+#endif // _LIBCPP_STD_VER < 23
 
 constexpr bool testReturnTypes() {
-  {
-    int *x[2];
-    ASSERT_SAME_TYPE(decltype(std::ranges::end(x)), int**);
-    ASSERT_SAME_TYPE(decltype(std::ranges::cend(x)), int* const*);
-  }
-  {
-    int x[2][2];
-    ASSERT_SAME_TYPE(decltype(std::ranges::end(x)), int(*)[2]);
-    ASSERT_SAME_TYPE(decltype(std::ranges::cend(x)), const int(*)[2]);
-  }
-  {
-    struct Different {
-      char *begin();
-      sentinel_wrapper<char*>& end();
-      short *begin() const;
-      sentinel_wrapper<short*>& end() const;
-    } x;
-    ASSERT_SAME_TYPE(decltype(std::ranges::end(x)), sentinel_wrapper<char*>);
-    ASSERT_SAME_TYPE(decltype(std::ranges::cend(x)), sentinel_wrapper<short*>);
-  }
+  int* a[2];
+  int b[2][2];
+  struct Different {
+    char* begin();
+    sentinel_wrapper<char*>& end();
+    short* begin() const;
+    sentinel_wrapper<short*>& end() const;
+  } c;
+
+  ASSERT_SAME_TYPE(decltype(std::ranges::end(a)), int**);
+  ASSERT_SAME_TYPE(decltype(std::ranges::end(b)), int(*)[2]);
+  ASSERT_SAME_TYPE(decltype(std::ranges::end(c)), sentinel_wrapper<char*>);
+
+#if _LIBCPP_STD_VER < 23
+  ASSERT_SAME_TYPE(decltype(std::ranges::cend(a)), int* const*);
+  ASSERT_SAME_TYPE(decltype(std::ranges::cend(b)), const int(*)[2]);
+  ASSERT_SAME_TYPE(decltype(std::ranges::cend(c)), sentinel_wrapper<short*>);
+#endif // _LIBCPP_STD_VER < 23
+
   return true;
 }
 
 constexpr bool testArray() {
   int a[2];
-  assert(std::ranges::end(a) == a + 2);
-  assert(std::ranges::cend(a) == a + 2);
-
   int b[2][2];
-  assert(std::ranges::end(b) == b + 2);
-  assert(std::ranges::cend(b) == b + 2);
-
   EndMember c[2];
+
+  assert(std::ranges::end(a) == a + 2);
+  assert(std::ranges::end(b) == b + 2);
   assert(std::ranges::end(c) == c + 2);
+
+#if _LIBCPP_STD_VER < 23
+  assert(std::ranges::cend(a) == a + 2);
+  assert(std::ranges::cend(b) == b + 2);
   assert(std::ranges::cend(c) == c + 2);
+#endif // _LIBCPP_STD_VER < 23
 
   return true;
 }
@@ -127,8 +136,10 @@ struct NonConstEndMember {
 };
 static_assert( std::is_invocable_v<RangeEndT,  NonConstEndMember &>);
 static_assert(!std::is_invocable_v<RangeEndT,  NonConstEndMember const&>);
+#if _LIBCPP_STD_VER < 23
 static_assert(!std::is_invocable_v<RangeCEndT, NonConstEndMember &>);
 static_assert(!std::is_invocable_v<RangeCEndT, NonConstEndMember const&>);
+#endif // _LIBCPP_STD_VER < 23
 
 struct EnabledBorrowingEndMember {
   constexpr int *begin() const { return nullptr; }
@@ -160,24 +171,24 @@ struct EmptyPtrEndMember {
 
 constexpr bool testEndMember() {
   EndMember a;
-  assert(std::ranges::end(a) == &a.x);
-  assert(std::ranges::cend(a) == &a.x);
-
   NonConstEndMember b;
-  assert(std::ranges::end(b) == &b.x);
-  static_assert(!std::is_invocable_v<RangeCEndT, decltype((b))>);
-
   EnabledBorrowingEndMember c;
-  assert(std::ranges::end(std::move(c)) == &globalBuff[0]);
-  assert(std::ranges::cend(std::move(c)) == &globalBuff[0]);
-
   EndMemberFunction d;
-  assert(std::ranges::end(d) == &d.x);
-  assert(std::ranges::cend(d) == &d.x);
-
   EmptyPtrEndMember e;
+
+  assert(std::ranges::end(a) == &a.x);
+  assert(std::ranges::end(b) == &b.x);
+  assert(std::ranges::end(std::move(c)) == &globalBuff[0]);
+  assert(std::ranges::end(d) == &d.x);
   assert(std::ranges::end(e) == &e.x);
+
+#if _LIBCPP_STD_VER < 23
+  assert(std::ranges::cend(a) == &a.x);
+  static_assert(!std::is_invocable_v<RangeCEndT, decltype((b))>);
+  assert(std::ranges::cend(std::move(c)) == &globalBuff[0]);
+  assert(std::ranges::cend(d) == &d.x);
   assert(std::ranges::cend(e) == &e.x);
+#endif // _LIBCPP_STD_VER < 23
 
   return true;
 }
@@ -194,8 +205,10 @@ static_assert(!std::is_invocable_v<RangeEndT, EndFunction &&>);
 static_assert( std::is_invocable_v<RangeEndT,  EndFunction const&>);
 static_assert(!std::is_invocable_v<RangeEndT,  EndFunction &&>);
 static_assert(std::is_invocable_v<RangeEndT, EndFunction&>); // Ill-formed before P2602R2 Poison Pills are Too Toxic
+#if _LIBCPP_STD_VER < 23
 static_assert( std::is_invocable_v<RangeCEndT, EndFunction const&>);
 static_assert( std::is_invocable_v<RangeCEndT, EndFunction &>);
+#endif // _LIBCPP_STD_VER < 23
 
 struct EndFunctionReturnsInt {
   friend constexpr int begin(EndFunctionReturnsInt const&);
@@ -230,7 +243,9 @@ struct EndFunctionByValue {
   friend constexpr int *begin(EndFunctionByValue) { return nullptr; }
   friend constexpr int *end(EndFunctionByValue) { return &globalBuff[1]; }
 };
+#if _LIBCPP_STD_VER < 23
 static_assert(!std::is_invocable_v<RangeCEndT, EndFunctionByValue>);
+#endif // _LIBCPP_STD_VER < 23
 
 struct EndFunctionEnabledBorrowing {
   friend constexpr int *begin(EndFunctionEnabledBorrowing) { return nullptr; }
@@ -268,61 +283,63 @@ struct BeginMemberEndFunction {
 
 constexpr bool testEndFunction() {
   const EndFunction a{};
-  assert(std::ranges::end(a) == &a.x);
-  assert(std::ranges::cend(a) == &a.x);
   EndFunction aa{};
-  assert(std::ranges::end(aa) == &aa.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic
-  assert(std::ranges::cend(aa) == &aa.x);
-
   EndFunctionByValue b;
-  assert(std::ranges::end(b) == &globalBuff[1]);
-  assert(std::ranges::cend(b) == &globalBuff[1]);
-
   EndFunctionEnabledBorrowing c;
-  assert(std::ranges::end(std::move(c)) == &globalBuff[2]);
-  assert(std::ranges::cend(std::move(c)) == &globalBuff[2]);
-
   const EndFunctionReturnsEmptyPtr d{};
-  assert(std::ranges::end(d) == &d.x);
-  assert(std::ranges::cend(d) == &d.x);
   EndFunctionReturnsEmptyPtr dd{};
-  assert(std::ranges::end(dd) == &dd.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic
-  assert(std::ranges::cend(dd) == &dd.x);
-
   const EndFunctionWithDataMember e{};
-  assert(std::ranges::end(e) == &e.x);
-  assert(std::ranges::cend(e) == &e.x);
   EndFunctionWithDataMember ee{};
-  assert(std::ranges::end(ee) == &ee.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic
-  assert(std::ranges::cend(ee) == &ee.x);
-
   const EndFunctionWithPrivateEndMember f{};
-  assert(std::ranges::end(f) == &f.y);
-  assert(std::ranges::cend(f) == &f.y);
   EndFunctionWithPrivateEndMember ff{};
-  assert(std::ranges::end(ff) == &ff.y); // Ill-formed before P2602R2 Poison Pills are Too Toxic
-  assert(std::ranges::cend(ff) == &ff.y);
-
   const BeginMemberEndFunction g{};
-  assert(std::ranges::end(g) == &g.x);
-  assert(std::ranges::cend(g) == &g.x);
   BeginMemberEndFunction gg{};
+
+  assert(std::ranges::end(a) == &a.x);
+  assert(std::ranges::end(aa) == &aa.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic
+  assert(std::ranges::end(b) == &globalBuff[1]);
+  assert(std::ranges::end(std::move(c)) == &globalBuff[2]);
+  assert(std::ranges::end(d) == &d.x);
+  assert(std::ranges::end(dd) == &dd.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic
+  assert(std::ranges::end(e) == &e.x);
+  assert(std::ranges::end(ee) == &ee.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic
+  assert(std::ranges::end(f) == &f.y);
+  assert(std::ranges::end(ff) == &ff.y); // Ill-formed before P2602R2 Poison Pills are Too Toxic
+  assert(std::ranges::end(g) == &g.x);
   assert(std::ranges::end(gg) == &gg.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic
+
+#if _LIBCPP_STD_VER < 23
+  assert(std::ranges::cend(a) == &a.x);
+  assert(std::ranges::cend(aa) == &aa.x);
+  assert(std::ranges::cend(b) == &globalBuff[1]);
+  assert(std::ranges::cend(std::move(c)) == &globalBuff[2]);
+  assert(std::ranges::cend(d) == &d.x);
+  assert(std::ranges::cend(dd) == &dd.x);
+  assert(std::ranges::cend(e) == &e.x);
+  assert(std::ranges::cend(ee) == &ee.x);
+  assert(std::ranges::cend(f) == &f.y);
+  assert(std::ranges::cend(ff) == &ff.y);
+  assert(std::ranges::cend(g) == &g.x);
   assert(std::ranges::cend(gg) == &gg.x);
+#endif // _LIBCPP_STD_VER < 23
 
   return true;
 }
 
 
 ASSERT_NOEXCEPT(std::ranges::end(std::declval<int (&)[10]>()));
+#if _LIBCPP_STD_VER < 23
 ASSERT_NOEXCEPT(std::ranges::cend(std::declval<int (&)[10]>()));
+#endif // _LIBCPP_STD_VER < 23
 
 struct NoThrowMemberEnd {
   ThrowingIterator<int> begin() const;
   ThrowingIterator<int> end() const noexcept; // auto(t.end()) doesn't throw
 } ntme;
 static_assert(noexcept(std::ranges::end(ntme)));
+#if _LIBCPP_STD_VER < 23
 static_assert(noexcept(std::ranges::cend(ntme)));
+#endif // _LIBCPP_STD_VER < 23
 
 struct NoThrowADLEnd {
   ThrowingIterator<int> begin() const;
@@ -330,29 +347,37 @@ struct NoThrowADLEnd {
   friend ThrowingIterator<int> end(const NoThrowADLEnd&) noexcept;
 } ntae;
 static_assert(noexcept(std::ranges::end(ntae)));
+#if _LIBCPP_STD_VER < 23
 static_assert(noexcept(std::ranges::cend(ntae)));
+#endif // _LIBCPP_STD_VER < 23
 
 struct NoThrowMemberEndReturnsRef {
   ThrowingIterator<int> begin() const;
   ThrowingIterator<int>& end() const noexcept; // auto(t.end()) may throw
 } ntmerr;
 static_assert(!noexcept(std::ranges::end(ntmerr)));
+#if _LIBCPP_STD_VER < 23
 static_assert(!noexcept(std::ranges::cend(ntmerr)));
+#endif // _LIBCPP_STD_VER < 23
 
 struct EndReturnsArrayRef {
     auto begin() const noexcept -> int(&)[10];
     auto end() const noexcept -> int(&)[10];
 } erar;
 static_assert(noexcept(std::ranges::end(erar)));
+#if _LIBCPP_STD_VER < 23
 static_assert(noexcept(std::ranges::cend(erar)));
+#endif // _LIBCPP_STD_VER < 23
 
 // Test ADL-proofing.
 struct Incomplete;
 template<class T> struct Holder { T t; };
 static_assert(!std::is_invocable_v<RangeEndT, Holder<Incomplete>*>);
 static_assert(!std::is_invocable_v<RangeEndT, Holder<Incomplete>*&>);
+#if _LIBCPP_STD_VER < 23
 static_assert(!std::is_invocable_v<RangeCEndT, Holder<Incomplete>*>);
 static_assert(!std::is_invocable_v<RangeCEndT, Holder<Incomplete>*&>);
+#endif // _LIBCPP_STD_VER < 23
 
 int main(int, char**) {
   static_assert(testReturnTypes());
diff --git a/libcxx/test/std/ranges/range.access/rbegin.pass.cpp b/libcxx/test/std/ranges/range.access/rbegin.pass.cpp
index d34ee9f2da0a7..a168889d5fa20 100644
--- a/libcxx/test/std/ranges/range.access/rbegin.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/rbegin.pass.cpp
@@ -19,9 +19,9 @@
 #include "test_iterators.h"
 
 using RangeRBeginT = decltype(std::ranges::rbegin);
-#if _LIBCPP_STD_VER <= 23
+#if _LIBCPP_STD_VER < 23
 using RangeCRBeginT = decltype(std::ranges::crbegin);
-#endif // _LIBCPP_STD_VER <= 23
+#endif // _LIBCPP_STD_VER < 23
 
 static int globalBuff[8];
 
@@ -29,12 +29,12 @@ 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 (&)[]>);
-#if _LIBCPP_STD_VER <= 23
+#if _LIBCPP_STD_VER < 23
 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 (&)[]>);
-#endif // _LIBCPP_STD_VER <= 23
+#endif // _LIBCPP_STD_VER < 23
 
 struct Incomplete;
 
@@ -43,28 +43,28 @@ static_assert(!std::is_invocable_v<RangeRBeginT, const Incomplete (&&)[]>);
 static_assert(!std::is_invocable_v<RangeRBeginT, Incomplete(&&)[10]>);
 static_assert(!std::is_invocable_v<RangeRBeginT, const Incomplete(&&)[10]>);
 
-#if _LIBCPP_STD_VER <= 23
+#if _LIBCPP_STD_VER < 23
 static_assert(!std::is_invocable_v<RangeCRBeginT, Incomplete (&&)[]>);
 static_assert(!std::is_invocable_v<RangeCRBeginT, const Incomplete (&&)[]>);
 static_assert(!std::is_invocable_v<RangeCRBeginT, Incomplete(&&)[10]>);
 static_assert(!std::is_invocable_v<RangeCRBeginT, const Incomplete(&&)[10]>);
-#endif // _LIBCPP_STD_VER <= 23
+#endif // _LIBCPP_STD_VER < 23
 
 // 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(&)[]>);
-#if _LIBCPP_STD_VER <= 23
+#if _LIBCPP_STD_VER < 23
 LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeCRBeginT, Incomplete(&)[]>);
 LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeCRBeginT, const Incomplete(&)[]>);
-#endif // _LIBCPP_STD_VER <= 23
+#endif // _LIBCPP_STD_VER < 23
 
 // 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]>);
-#if _LIBCPP_STD_VER <= 23
+#if _LIBCPP_STD_VER < 23
 LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeCRBeginT, Incomplete(&)[10]>);
 LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeCRBeginT, const Incomplete(&)[10]>);
-#endif // _LIBCPP_STD_VER <= 23
+#endif // _LIBCPP_STD_VER < 23
 
 struct RBeginMember {
   int x;
@@ -76,12 +76,12 @@ 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&&>);
-#if _LIBCPP_STD_VER <= 23
+#if _LIBCPP_STD_VER < 23
 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&&>);
-#endif // _LIBCPP_STD_VER <= 23
+#endif // _LIBCPP_STD_VER < 23
 
 constexpr bool testReturnTypes() {
   int* a[2];
@@ -91,15 +91,15 @@ constexpr bool testReturnTypes() {
     short*& rbegin() const;
   } c;
 
-  ASSERT_SAME_TYPE(decltype(std::ranges::rbegin(x)), std::reverse_iterator<int**>);
-  ASSERT_SAME_TYPE(decltype(std::ranges::rbegin(x)), std::reverse_iterator<int(*)[2]>);
-  ASSERT_SAME_TYPE(decltype(std::ranges::rbegin(x)), char*);
+  ASSERT_SAME_TYPE(decltype(std::ranges::rbegin(a)), std::reverse_iterator<int**>);
+  ASSERT_SAME_TYPE(decltype(std::ranges::rbegin(b)), std::reverse_iterator<int(*)[2]>);
+  ASSERT_SAME_TYPE(decltype(std::ranges::rbegin(c)), char*);
 
-#if _LIBCPP_STD_VER <= 23
-  ASSERT_SAME_TYPE(decltype(std::ranges::crbegin(x)), std::reverse_iterator<int* const*>);
-  ASSERT_SAME_TYPE(decltype(std::ranges::crbegin(x)), std::reverse_iterator<const int(*)[2]>);
-  ASSERT_SAME_TYPE(decltype(std::ranges::crbegin(x)), short*);
-#endif // _LIBCPP_STD_VER <= 23
+#if _LIBCPP_STD_VER < 23
+  ASSERT_SAME_TYPE(decltype(std::ranges::crbegin(a)), std::reverse_iterator<int* const*>);
+  ASSERT_SAME_TYPE(decltype(std::ranges::crbegin(b)), std::reverse_iterator<const int(*)[2]>);
+  ASSERT_SAME_TYPE(decltype(std::ranges::crbegin(c)), short*);
+#endif // _LIBCPP_STD_VER < 23
 
   return true;
 }
@@ -113,11 +113,11 @@ constexpr bool testArray() {
   assert(std::ranges::rbegin(b).base() == b + 2);
   assert(std::ranges::rbegin(c).base() == c + 2);
 
-#if _LIBCPP_STD_VER <= 23
+#if _LIBCPP_STD_VER < 23
   assert(std::ranges::crbegin(a).base() == a + 2);
   assert(std::ranges::crbegin(b).base() == b + 2);
   assert(std::ranges::crbegin(c).base() == c + 2);
-#endif // _LIBCPP_STD_VER <= 23
+#endif // _LIBCPP_STD_VER < 23
 
   return true;
 }
@@ -144,10 +144,10 @@ struct NonConstRBeginMember {
 };
 static_assert( std::is_invocable_v<RangeRBeginT,  NonConstRBeginMember &>);
 static_assert(!std::is_invocable_v<RangeRBeginT,  NonConstRBeginMember const&>);
-#if _LIBCPP_STD_VER <= 23
+#if _LIBCPP_STD_VER < 23
 static_assert(!std::is_invocable_v<RangeCRBeginT, NonConstRBeginMember &>);
 static_assert(!std::is_invocable_v<RangeCRBeginT, NonConstRBeginMember const&>);
-#endif // _LIBCPP_STD_VER <= 23
+#endif // _LIBCPP_STD_VER < 23
 
 struct EnabledBorrowingRBeginMember {
   constexpr int *rbegin() const { return globalBuff; }
@@ -182,7 +182,7 @@ constexpr bool testRBeginMember() {
   assert(std::ranges::rbegin(d) == &d.x);
   assert(std::ranges::rbegin(e) == &e.x);
 
-#if _LIBCPP_STD_VER <= 23
+#if _LIBCPP_STD_VER < 23
   assert(std::ranges::crbegin(a) == &a.x);
   static_assert(!std::is_invocable_v<RangeCRBeginT, RBeginMember&&>);
   static_assert(!std::is_invocable_v<RangeCRBeginT, NonConstRBeginMember&>);
@@ -190,7 +190,7 @@ constexpr bool testRBeginMember() {
   assert(std::ranges::crbegin(std::move(c)) == globalBuff);
   assert(std::ranges::crbegin(d) == &d.x);
   assert(std::ranges::crbegin(e) == &e.x);
-#endif // _LIBCPP_STD_VER <= 23
+#endif // _LIBCPP_STD_VER < 23
 
   return true;
 }
@@ -204,10 +204,10 @@ 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&>); // Ill-formed before P2602R2 Poison Pills are Too Toxic
-#if _LIBCPP_STD_VER <= 23
+#if _LIBCPP_STD_VER < 23
 static_assert( std::is_invocable_v<RangeCRBeginT, RBeginFunction const&>);
 static_assert( std::is_invocable_v<RangeCRBeginT, RBeginFunction &>);
-#endif // _LIBCPP_STD_VER <= 23
+#endif // _LIBCPP_STD_VER < 23
 
 struct RBeginFunctionReturnsInt {
   friend int rbegin(RBeginFunctionReturnsInt const&);
@@ -231,12 +231,12 @@ struct RBeginFunctionReturnsPtrConvertible {
 };
 static_assert(!std::is_invocable_v<RangeRBeginT, RBeginFunctionReturnsPtrConvertible const&>);
 
-#if _LIBCPP_STD_VER <= 23
 struct RBeginFunctionByValue {
   friend constexpr int *rbegin(RBeginFunctionByValue) { return globalBuff + 1; }
 };
+#if _LIBCPP_STD_VER < 23
 static_assert(!std::is_invocable_v<RangeCRBeginT, RBeginFunctionByValue>);
-#endif // _LIBCPP_STD_VER <= 23
+#endif // _LIBCPP_STD_VER < 23
 
 struct RBeginFunctionEnabledBorrowing {
   friend constexpr int *rbegin(RBeginFunctionEnabledBorrowing) { return globalBuff + 2; }
@@ -290,7 +290,7 @@ constexpr bool testRBeginFunction() {
   assert(std::ranges::rbegin(f) == &f.y); // Ill-formed before P2602R2 Poison Pills are Too Toxic
   assert(std::ranges::rbegin(ff) == &ff.y);
 
-#if _LIBCPP_STD_VER <= 23
+#if _LIBCPP_STD_VER < 23
   assert(std::ranges::crbegin(a) == &a.x);
   assert(std::ranges::crbegin(aa) == &aa.x);
   assert(std::ranges::crbegin(b) == globalBuff + 1);
@@ -303,7 +303,7 @@ constexpr bool testRBeginFunction() {
   assert(std::ranges::crbegin(ee) == &ee.x);
   assert(std::ranges::crbegin(f) == &f.y);
   assert(std::ranges::crbegin(ff) == &ff.y);
-#endif // _LIBCPP_STD_VER <= 23
+#endif // _LIBCPP_STD_VER < 23
 
   return true;
 }
@@ -319,9 +319,9 @@ struct MemberBeginEnd {
 };
 static_assert( std::is_invocable_v<RangeRBeginT, MemberBeginEnd&>);
 static_assert( std::is_invocable_v<RangeRBeginT, MemberBeginEnd const&>);
-#if _LIBCPP_STD_VER <= 23
+#if _LIBCPP_STD_VER < 23
 static_assert( std::is_invocable_v<RangeCRBeginT, MemberBeginEnd const&>);
-#endif // _LIBCPP_STD_VER <= 23
+#endif // _LIBCPP_STD_VER < 23
 
 struct FunctionBeginEnd {
   int b, e;
@@ -339,9 +339,9 @@ struct FunctionBeginEnd {
 };
 static_assert( std::is_invocable_v<RangeRBeginT, FunctionBeginEnd&>);
 static_assert( std::is_invocable_v<RangeRBeginT, FunctionBeginEnd const&>);
-#if _LIBCPP_STD_VER <= 23
+#if _LIBCPP_STD_VER < 23
 static_assert( std::is_invocable_v<RangeCRBeginT, FunctionBeginEnd const&>);
-#endif // _LIBCPP_STD_VER <= 23
+#endif // _LIBCPP_STD_VER < 23
 
 struct MemberBeginFunctionEnd {
   int b, e;
@@ -357,9 +357,9 @@ struct MemberBeginFunctionEnd {
 };
 static_assert( std::is_invocable_v<RangeRBeginT, MemberBeginFunctionEnd&>);
 static_assert( std::is_invocable_v<RangeRBeginT, MemberBeginFunctionEnd const&>);
-#if _LIBCPP_STD_VER <= 23
+#if _LIBCPP_STD_VER < 23
 static_assert( std::is_invocable_v<RangeCRBeginT, MemberBeginFunctionEnd const&>);
-#endif // _LIBCPP_STD_VER <= 23
+#endif // _LIBCPP_STD_VER < 23
 
 struct FunctionBeginMemberEnd {
   int b, e;
@@ -375,77 +375,77 @@ struct FunctionBeginMemberEnd {
 };
 static_assert( std::is_invocable_v<RangeRBeginT, FunctionBeginMemberEnd&>);
 static_assert( std::is_invocable_v<RangeRBeginT, FunctionBeginMemberEnd const&>);
-#if _LIBCPP_STD_VER <= 23
+#if _LIBCPP_STD_VER < 23
 static_assert( std::is_invocable_v<RangeCRBeginT, FunctionBeginMemberEnd const&>);
-#endif // _LIBCPP_STD_VER <= 23
+#endif // _LIBCPP_STD_VER < 23
 
 struct MemberBeginEndDifferentTypes {
   bidirectional_iterator<int*> begin();
   bidirectional_iterator<const int*> end();
 };
 static_assert(!std::is_invocable_v<RangeRBeginT, MemberBeginEndDifferentTypes&>);
-#if _LIBCPP_STD_VER <= 23
+#if _LIBCPP_STD_VER < 23
 static_assert(!std::is_invocable_v<RangeCRBeginT, MemberBeginEndDifferentTypes&>);
-#endif // _LIBCPP_STD_VER <= 23
+#endif // _LIBCPP_STD_VER < 23
 
 struct FunctionBeginEndDifferentTypes {
   friend bidirectional_iterator<int*> begin(FunctionBeginEndDifferentTypes&);
   friend bidirectional_iterator<const int*> end(FunctionBeginEndDifferentTypes&);
 };
 static_assert(!std::is_invocable_v<RangeRBeginT, FunctionBeginEndDifferentTypes&>);
-#if _LIBCPP_STD_VER <= 23
+#if _LIBCPP_STD_VER < 23
 static_assert(!std::is_invocable_v<RangeCRBeginT, FunctionBeginEndDifferentTypes&>);
-#endif // _LIBCPP_STD_VER <= 23
+#endif // _LIBCPP_STD_VER < 23
 
 struct MemberBeginEndForwardIterators {
   forward_iterator<int*> begin();
   forward_iterator<int*> end();
 };
 static_assert(!std::is_invocable_v<RangeRBeginT, MemberBeginEndForwardIterators&>);
-#if _LIBCPP_STD_VER <= 23
+#if _LIBCPP_STD_VER < 23
 static_assert(!std::is_invocable_v<RangeCRBeginT, MemberBeginEndForwardIterators&>);
-#endif // _LIBCPP_STD_VER <= 23
+#endif // _LIBCPP_STD_VER < 23
 
 struct FunctionBeginEndForwardIterators {
   friend forward_iterator<int*> begin(FunctionBeginEndForwardIterators&);
   friend forward_iterator<int*> end(FunctionBeginEndForwardIterators&);
 };
 static_assert(!std::is_invocable_v<RangeRBeginT, FunctionBeginEndForwardIterators&>);
-#if _LIBCPP_STD_VER <= 23
+#if _LIBCPP_STD_VER < 23
 static_assert(!std::is_invocable_v<RangeCRBeginT, FunctionBeginEndForwardIterators&>);
-#endif // _LIBCPP_STD_VER <= 23
+#endif // _LIBCPP_STD_VER < 23
 
 struct MemberBeginOnly {
   bidirectional_iterator<int*> begin() const;
 };
 static_assert(!std::is_invocable_v<RangeRBeginT, MemberBeginOnly&>);
-#if _LIBCPP_STD_VER <= 23
+#if _LIBCPP_STD_VER < 23
 static_assert(!std::is_invocable_v<RangeCRBeginT, MemberBeginOnly&>);
-#endif // _LIBCPP_STD_VER <= 23
+#endif // _LIBCPP_STD_VER < 23
 
 struct FunctionBeginOnly {
   friend bidirectional_iterator<int*> begin(FunctionBeginOnly&);
 };
 static_assert(!std::is_invocable_v<RangeRBeginT, FunctionBeginOnly&>);
-#if _LIBCPP_STD_VER <= 23
+#if _LIBCPP_STD_VER < 23
 static_assert(!std::is_invocable_v<RangeCRBeginT, FunctionBeginOnly&>);
-#endif // _LIBCPP_STD_VER <= 23
+#endif // _LIBCPP_STD_VER < 23
 
 struct MemberEndOnly {
   bidirectional_iterator<int*> end() const;
 };
 static_assert(!std::is_invocable_v<RangeRBeginT, MemberEndOnly&>);
-#if _LIBCPP_STD_VER <= 23
+#if _LIBCPP_STD_VER < 23
 static_assert(!std::is_invocable_v<RangeCRBeginT, MemberEndOnly&>);
-#endif // _LIBCPP_STD_VER <= 23
+#endif // _LIBCPP_STD_VER < 23
 
 struct FunctionEndOnly {
   friend bidirectional_iterator<int*> end(FunctionEndOnly&);
 };
 static_assert(!std::is_invocable_v<RangeRBeginT, FunctionEndOnly&>);
-#if _LIBCPP_STD_VER <= 23
+#if _LIBCPP_STD_VER < 23
 static_assert(!std::is_invocable_v<RangeCRBeginT, FunctionEndOnly&>);
-#endif // _LIBCPP_STD_VER <= 23
+#endif // _LIBCPP_STD_VER < 23
 
 // Make sure there is no clash between the following cases:
 // - the case that handles classes defining member `rbegin` and `rend` functions;
@@ -458,10 +458,10 @@ struct MemberBeginAndRBegin {
 };
 static_assert(std::is_invocable_v<RangeRBeginT, MemberBeginAndRBegin&>);
 static_assert( std::same_as<std::invoke_result_t<RangeRBeginT, MemberBeginAndRBegin&>, int*>);
-#if _LIBCPP_STD_VER <= 23
+#if _LIBCPP_STD_VER < 23
 static_assert(std::is_invocable_v<RangeCRBeginT, MemberBeginAndRBegin&>);
 static_assert( std::same_as<std::invoke_result_t<RangeCRBeginT, MemberBeginAndRBegin&>, int*>);
-#endif // _LIBCPP_STD_VER <= 23
+#endif // _LIBCPP_STD_VER < 23
 
 constexpr bool testBeginEnd() {
   MemberBeginEnd a{};
@@ -482,7 +482,7 @@ constexpr bool testBeginEnd() {
   assert(base(std::ranges::rbegin(d).base()) == &d.e);
   assert(base(std::ranges::rbegin(dd).base()) == &dd.ce);
 
-#if _LIBCPP_STD_VER <= 23
+#if _LIBCPP_STD_VER < 23
   assert(base(std::ranges::crbegin(a).base()) == &a.ce);
   assert(base(std::ranges::crbegin(aa).base()) == &aa.ce);
   assert(base(std::ranges::crbegin(b).base()) == &b.ce);
@@ -491,77 +491,77 @@ constexpr bool testBeginEnd() {
   assert(base(std::ranges::crbegin(cc).base()) == &cc.ce);
   assert(base(std::ranges::crbegin(d).base()) == &d.ce);
   assert(base(std::ranges::crbegin(dd).base()) == &dd.ce);
-#endif // _LIBCPP_STD_VER <= 23
+#endif // _LIBCPP_STD_VER < 23
 
   return true;
 }
 
 
 ASSERT_NOEXCEPT(std::ranges::rbegin(std::declval<int (&)[10]>()));
-#if _LIBCPP_STD_VER <= 23
+#if _LIBCPP_STD_VER < 23
 ASSERT_NOEXCEPT(std::ranges::crbegin(std::declval<int (&)[10]>()));
-#endif // _LIBCPP_STD_VER <= 23
+#endif // _LIBCPP_STD_VER < 23
 
 struct NoThrowMemberRBegin {
   ThrowingIterator<int> rbegin() const noexcept; // auto(t.rbegin()) doesn't throw
 } ntmb;
 static_assert(noexcept(std::ranges::rbegin(ntmb)));
-#if _LIBCPP_STD_VER <= 23
+#if _LIBCPP_STD_VER < 23
 static_assert(noexcept(std::ranges::crbegin(ntmb)));
-#endif // _LIBCPP_STD_VER <= 23
+#endif // _LIBCPP_STD_VER < 23
 
 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)));
-#if _LIBCPP_STD_VER <= 23
+#if _LIBCPP_STD_VER < 23
 static_assert(noexcept(std::ranges::crbegin(ntab)));
-#endif // _LIBCPP_STD_VER <= 23
+#endif // _LIBCPP_STD_VER < 23
 
 struct NoThrowMemberRBeginReturnsRef {
   ThrowingIterator<int>& rbegin() const noexcept; // auto(t.rbegin()) may throw
 } ntmbrr;
 static_assert(!noexcept(std::ranges::rbegin(ntmbrr)));
-#if _LIBCPP_STD_VER <= 23
+#if _LIBCPP_STD_VER < 23
 static_assert(!noexcept(std::ranges::crbegin(ntmbrr)));
-#endif // _LIBCPP_STD_VER <= 23
+#endif // _LIBCPP_STD_VER < 23
 
 struct RBeginReturnsArrayRef {
     auto rbegin() const noexcept -> int(&)[10];
 } brar;
 static_assert(noexcept(std::ranges::rbegin(brar)));
-#if _LIBCPP_STD_VER <= 23
+#if _LIBCPP_STD_VER < 23
 static_assert(noexcept(std::ranges::crbegin(brar)));
-#endif // _LIBCPP_STD_VER <= 23
+#endif // _LIBCPP_STD_VER < 23
 
 struct NoThrowBeginThrowingEnd {
   int* begin() const noexcept;
   int* end() const;
 } ntbte;
 static_assert(!noexcept(std::ranges::rbegin(ntbte)));
-#if _LIBCPP_STD_VER <= 23
+#if _LIBCPP_STD_VER < 23
 static_assert(!noexcept(std::ranges::crbegin(ntbte)));
-#endif // _LIBCPP_STD_VER <= 23
+#endif // _LIBCPP_STD_VER < 23
 
 struct NoThrowEndThrowingBegin {
   int* begin() const;
   int* end() const noexcept;
 } ntetb;
 static_assert(noexcept(std::ranges::rbegin(ntetb)));
-#if _LIBCPP_STD_VER <= 23
+#if _LIBCPP_STD_VER < 23
 static_assert(noexcept(std::ranges::crbegin(ntetb)));
-#endif // _LIBCPP_STD_VER <= 23
+#endif // _LIBCPP_STD_VER < 23
 
 // 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>*&>);
-#if _LIBCPP_STD_VER <= 23
+#if _LIBCPP_STD_VER < 23
 static_assert(!std::is_invocable_v<RangeCRBeginT, Holder<Incomplete>*>);
 static_assert(!std::is_invocable_v<RangeCRBeginT, Holder<Incomplete>*&>);
-#endif // _LIBCPP_STD_VER <= 23
+#endif // _LIBCPP_STD_VER < 23
 
 int main(int, char**) {
   static_assert(testReturnTypes());
diff --git a/libcxx/test/std/ranges/range.access/rend.pass.cpp b/libcxx/test/std/ranges/range.access/rend.pass.cpp
index 55c2e084d25c3..6029d7f960d88 100644
--- a/libcxx/test/std/ranges/range.access/rend.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/rend.pass.cpp
@@ -19,9 +19,9 @@
 #include "test_iterators.h"
 
 using RangeREndT = decltype(std::ranges::rend);
-#if _LIBCPP_STD_VER <= 23
+#if _LIBCPP_STD_VER < 23
 using RangeCREndT = decltype(std::ranges::crend);
-#endif // _LIBCPP_STD_VER <= 23
+#endif // _LIBCPP_STD_VER < 23
 
 static int globalBuff[8];
 
@@ -29,20 +29,20 @@ 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]>);
-#if _LIBCPP_STD_VER <= 23
+#if _LIBCPP_STD_VER < 23
 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]>);
-#endif // _LIBCPP_STD_VER <= 23
+#endif // _LIBCPP_STD_VER < 23
 
 struct Incomplete;
 static_assert(!std::is_invocable_v<RangeREndT, Incomplete(&&)[]>);
 static_assert(!std::is_invocable_v<RangeREndT, Incomplete(&&)[42]>);
-#if _LIBCPP_STD_VER <= 23
+#if _LIBCPP_STD_VER < 23
 static_assert(!std::is_invocable_v<RangeCREndT, Incomplete(&&)[]>);
 static_assert(!std::is_invocable_v<RangeCREndT, Incomplete(&&)[42]>);
-#endif // _LIBCPP_STD_VER <= 23
+#endif // _LIBCPP_STD_VER < 23
 
 struct REndMember {
   int x;
@@ -55,42 +55,32 @@ 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&&>);
-#if _LIBCPP_STD_VER <= 23
+#if _LIBCPP_STD_VER < 23
 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&&>);
-#endif // _LIBCPP_STD_VER <= 23
+#endif // _LIBCPP_STD_VER < 23
 
 constexpr bool testReturnTypes() {
-  {
-    int *x[2];
-    ASSERT_SAME_TYPE(decltype(std::ranges::rend(x)), std::reverse_iterator<int**>);
-#if _LIBCPP_STD_VER <= 23
-    ASSERT_SAME_TYPE(decltype(std::ranges::crend(x)), std::reverse_iterator<int* const*>);
-#endif // _LIBCPP_STD_VER <= 23
-  }
-
-  {
-    int x[2][2];
-    ASSERT_SAME_TYPE(decltype(std::ranges::rend(x)), std::reverse_iterator<int(*)[2]>);
-#if _LIBCPP_STD_VER <= 23
-    ASSERT_SAME_TYPE(decltype(std::ranges::crend(x)), std::reverse_iterator<const int(*)[2]>);
-#endif // _LIBCPP_STD_VER <= 23
-  }
-
-  {
-    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*>);
-#if _LIBCPP_STD_VER <= 23
-    ASSERT_SAME_TYPE(decltype(std::ranges::crend(x)), sentinel_wrapper<short*>);
-#endif // _LIBCPP_STD_VER <= 23
-  }
+  int* a[2];
+  int b[2][2];
+  struct Different {
+    char* rbegin();
+    sentinel_wrapper<char*>& rend();
+    short* rbegin() const;
+    sentinel_wrapper<short*>& rend() const;
+  } c;
+
+  ASSERT_SAME_TYPE(decltype(std::ranges::rend(a)), std::reverse_iterator<int**>);
+  ASSERT_SAME_TYPE(decltype(std::ranges::rend(b)), std::reverse_iterator<int(*)[2]>);
+  ASSERT_SAME_TYPE(decltype(std::ranges::rend(c)), sentinel_wrapper<char*>);
+
+#if _LIBCPP_STD_VER < 23
+  ASSERT_SAME_TYPE(decltype(std::ranges::crend(a)), std::reverse_iterator<int* const*>);
+  ASSERT_SAME_TYPE(decltype(std::ranges::crend(b)), std::reverse_iterator<const int(*)[2]>);
+  ASSERT_SAME_TYPE(decltype(std::ranges::crend(c)), sentinel_wrapper<short*>);
+#endif // _LIBCPP_STD_VER < 23
 
   return true;
 }
@@ -104,11 +94,11 @@ constexpr bool testArray() {
   assert(std::ranges::rend(b).base() == b);
   assert(std::ranges::rend(c).base() == c);
 
-#if _LIBCPP_STD_VER <= 23
+#if _LIBCPP_STD_VER < 23
   assert(std::ranges::crend(b).base() == b);
   assert(std::ranges::crend(a).base() == a);
   assert(std::ranges::crend(c).base() == c);
-#endif // _LIBCPP_STD_VER <= 23                                                                                        \
+#endif // _LIBCPP_STD_VER < 23                                                                                        \
        //
   return true;
 }
@@ -146,10 +136,10 @@ struct NonConstREndMember {
 };
 static_assert( std::is_invocable_v<RangeREndT,  NonConstREndMember &>);
 static_assert(!std::is_invocable_v<RangeREndT,  NonConstREndMember const&>);
-#if _LIBCPP_STD_VER <= 23
+#if _LIBCPP_STD_VER < 23
 static_assert(!std::is_invocable_v<RangeCREndT, NonConstREndMember &>);
 static_assert(!std::is_invocable_v<RangeCREndT, NonConstREndMember const&>);
-#endif // _LIBCPP_STD_VER <= 23
+#endif // _LIBCPP_STD_VER < 23
 
 struct EnabledBorrowingREndMember {
   constexpr int* rbegin() const { return nullptr; }
@@ -192,13 +182,13 @@ constexpr bool testREndMember() {
   assert(std::ranges::rend(d) == &d.x);
   assert(std::ranges::rend(e) == &e.x);
 
-#if _LIBCPP_STD_VER <= 23
+#if _LIBCPP_STD_VER < 23
   assert(std::ranges::crend(a) == &a.x);
   static_assert(!std::is_invocable_v<RangeCREndT, decltype((b))>);
   assert(std::ranges::crend(std::move(c)) == &globalBuff[0]);
   assert(std::ranges::crend(d) == &d.x);
   assert(std::ranges::crend(e) == &e.x);
-#endif // _LIBCPP_STD_VER <= 23
+#endif // _LIBCPP_STD_VER < 23
 
   return true;
 }
@@ -215,10 +205,10 @@ 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&>); // Ill-formed before P2602R2 Poison Pills are Too Toxic
-#if _LIBCPP_STD_VER <= 23
+#if _LIBCPP_STD_VER < 23
 static_assert( std::is_invocable_v<RangeCREndT, REndFunction const&>);
 static_assert( std::is_invocable_v<RangeCREndT, REndFunction &>);
-#endif // _LIBCPP_STD_VER <= 23
+#endif // _LIBCPP_STD_VER < 23
 
 struct REndFunctionReturnsInt {
   friend constexpr int rbegin(REndFunctionReturnsInt const&);
@@ -253,9 +243,9 @@ struct REndFunctionByValue {
   friend constexpr int* rbegin(REndFunctionByValue) { return nullptr; }
   friend constexpr int* rend(REndFunctionByValue) { return &globalBuff[1]; }
 };
-#if _LIBCPP_STD_VER <= 23
+#if _LIBCPP_STD_VER < 23
 static_assert(!std::is_invocable_v<RangeCREndT, REndFunctionByValue>);
-#endif // _LIBCPP_STD_VER <= 23
+#endif // _LIBCPP_STD_VER < 23
 
 struct REndFunctionEnabledBorrowing {
   friend constexpr int* rbegin(REndFunctionEnabledBorrowing) { return nullptr; }
@@ -316,7 +306,7 @@ constexpr bool testREndFunction() {
   assert(std::ranges::rend(g) == &g.x);
   assert(std::ranges::rend(gg) == &gg.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic
 
-#if _LIBCPP_STD_VER <= 23
+#if _LIBCPP_STD_VER < 23
   assert(std::ranges::crend(a) == &a.x);
   assert(std::ranges::crend(aa) == &aa.x);
   assert(std::ranges::crend(b) == &globalBuff[1]);
@@ -329,7 +319,7 @@ constexpr bool testREndFunction() {
   assert(std::ranges::crend(ff) == &ff.y);
   assert(std::ranges::crend(g) == &g.x);
   assert(std::ranges::crend(gg) == &gg.x);
-#endif // _LIBCPP_STD_VER <= 23
+#endif // _LIBCPP_STD_VER < 23
 
   return true;
 }
@@ -345,9 +335,9 @@ struct MemberBeginEnd {
 };
 static_assert( std::is_invocable_v<RangeREndT, MemberBeginEnd&>);
 static_assert( std::is_invocable_v<RangeREndT, MemberBeginEnd const&>);
-#if _LIBCPP_STD_VER <= 23
+#if _LIBCPP_STD_VER < 23
 static_assert( std::is_invocable_v<RangeCREndT, MemberBeginEnd const&>);
-#endif // _LIBCPP_STD_VER <= 23
+#endif // _LIBCPP_STD_VER < 23
 
 struct FunctionBeginEnd {
   int b, e;
@@ -365,9 +355,9 @@ struct FunctionBeginEnd {
 };
 static_assert( std::is_invocable_v<RangeREndT, FunctionBeginEnd&>);
 static_assert( std::is_invocable_v<RangeREndT, FunctionBeginEnd const&>);
-#if _LIBCPP_STD_VER <= 23
+#if _LIBCPP_STD_VER < 23
 static_assert( std::is_invocable_v<RangeCREndT, FunctionBeginEnd const&>);
-#endif // _LIBCPP_STD_VER <= 23
+#endif // _LIBCPP_STD_VER < 23
 
 struct MemberBeginFunctionEnd {
   int b, e;
@@ -383,9 +373,9 @@ struct MemberBeginFunctionEnd {
 };
 static_assert( std::is_invocable_v<RangeREndT, MemberBeginFunctionEnd&>);
 static_assert( std::is_invocable_v<RangeREndT, MemberBeginFunctionEnd const&>);
-#if _LIBCPP_STD_VER <= 23
+#if _LIBCPP_STD_VER < 23
 static_assert( std::is_invocable_v<RangeCREndT, MemberBeginFunctionEnd const&>);
-#endif // _LIBCPP_STD_VER <= 23
+#endif // _LIBCPP_STD_VER < 23
 
 struct FunctionBeginMemberEnd {
   int b, e;
@@ -401,77 +391,77 @@ struct FunctionBeginMemberEnd {
 };
 static_assert( std::is_invocable_v<RangeREndT, FunctionBeginMemberEnd&>);
 static_assert( std::is_invocable_v<RangeREndT, FunctionBeginMemberEnd const&>);
-#if _LIBCPP_STD_VER <= 23
+#if _LIBCPP_STD_VER < 23
 static_assert( std::is_invocable_v<RangeCREndT, FunctionBeginMemberEnd const&>);
-#endif // _LIBCPP_STD_VER <= 23
+#endif // _LIBCPP_STD_VER < 23
 
 struct MemberBeginEndDifferentTypes {
   bidirectional_iterator<int*> begin();
   bidirectional_iterator<const int*> end();
 };
 static_assert(!std::is_invocable_v<RangeREndT, MemberBeginEndDifferentTypes&>);
-#if _LIBCPP_STD_VER <= 23
+#if _LIBCPP_STD_VER < 23
 static_assert(!std::is_invocable_v<RangeCREndT, MemberBeginEndDifferentTypes&>);
-#endif // _LIBCPP_STD_VER <= 23
+#endif // _LIBCPP_STD_VER < 23
 
 struct FunctionBeginEndDifferentTypes {
   friend bidirectional_iterator<int*> begin(FunctionBeginEndDifferentTypes&);
   friend bidirectional_iterator<const int*> end(FunctionBeginEndDifferentTypes&);
 };
 static_assert(!std::is_invocable_v<RangeREndT, FunctionBeginEndDifferentTypes&>);
-#if _LIBCPP_STD_VER <= 23
+#if _LIBCPP_STD_VER < 23
 static_assert(!std::is_invocable_v<RangeCREndT, FunctionBeginEndDifferentTypes&>);
-#endif // _LIBCPP_STD_VER <= 23
+#endif // _LIBCPP_STD_VER < 23
 
 struct MemberBeginEndForwardIterators {
   forward_iterator<int*> begin();
   forward_iterator<int*> end();
 };
 static_assert(!std::is_invocable_v<RangeREndT, MemberBeginEndForwardIterators&>);
-#if _LIBCPP_STD_VER <= 23
+#if _LIBCPP_STD_VER < 23
 static_assert(!std::is_invocable_v<RangeCREndT, MemberBeginEndForwardIterators&>);
-#endif // _LIBCPP_STD_VER <= 23
+#endif // _LIBCPP_STD_VER < 23
 
 struct FunctionBeginEndForwardIterators {
   friend forward_iterator<int*> begin(FunctionBeginEndForwardIterators&);
   friend forward_iterator<int*> end(FunctionBeginEndForwardIterators&);
 };
 static_assert(!std::is_invocable_v<RangeREndT, FunctionBeginEndForwardIterators&>);
-#if _LIBCPP_STD_VER <= 23
+#if _LIBCPP_STD_VER < 23
 static_assert(!std::is_invocable_v<RangeCREndT, FunctionBeginEndForwardIterators&>);
-#endif // _LIBCPP_STD_VER <= 23
+#endif // _LIBCPP_STD_VER < 23
 
 struct MemberBeginOnly {
   bidirectional_iterator<int*> begin() const;
 };
 static_assert(!std::is_invocable_v<RangeREndT, MemberBeginOnly&>);
-#if _LIBCPP_STD_VER <= 23
+#if _LIBCPP_STD_VER < 23
 static_assert(!std::is_invocable_v<RangeCREndT, MemberBeginOnly&>);
-#endif // _LIBCPP_STD_VER <= 23
+#endif // _LIBCPP_STD_VER < 23
 
 struct FunctionBeginOnly {
   friend bidirectional_iterator<int*> begin(FunctionBeginOnly&);
 };
 static_assert(!std::is_invocable_v<RangeREndT, FunctionBeginOnly&>);
-#if _LIBCPP_STD_VER <= 23
+#if _LIBCPP_STD_VER < 23
 static_assert(!std::is_invocable_v<RangeCREndT, FunctionBeginOnly&>);
-#endif // _LIBCPP_STD_VER <= 23
+#endif // _LIBCPP_STD_VER < 23
 
 struct MemberEndOnly {
   bidirectional_iterator<int*> end() const;
 };
 static_assert(!std::is_invocable_v<RangeREndT, MemberEndOnly&>);
-#if _LIBCPP_STD_VER <= 23
+#if _LIBCPP_STD_VER < 23
 static_assert(!std::is_invocable_v<RangeCREndT, MemberEndOnly&>);
-#endif // _LIBCPP_STD_VER <= 23
+#endif // _LIBCPP_STD_VER < 23
 
 struct FunctionEndOnly {
   friend bidirectional_iterator<int*> end(FunctionEndOnly&);
 };
 static_assert(!std::is_invocable_v<RangeREndT, FunctionEndOnly&>);
-#if _LIBCPP_STD_VER <= 23
+#if _LIBCPP_STD_VER < 23
 static_assert(!std::is_invocable_v<RangeCREndT, FunctionEndOnly&>);
-#endif // _LIBCPP_STD_VER <= 23
+#endif // _LIBCPP_STD_VER < 23
 
 // Make sure there is no clash between the following cases:
 // - the case that handles classes defining member `rbegin` and `rend` functions;
@@ -484,10 +474,10 @@ struct MemberBeginAndRBegin {
 };
 static_assert(std::is_invocable_v<RangeREndT, MemberBeginAndRBegin&>);
 static_assert( std::same_as<std::invoke_result_t<RangeREndT, MemberBeginAndRBegin&>, int*>);
-#if _LIBCPP_STD_VER <= 23
+#if _LIBCPP_STD_VER < 23
 static_assert(std::is_invocable_v<RangeCREndT, MemberBeginAndRBegin&>);
 static_assert( std::same_as<std::invoke_result_t<RangeCREndT, MemberBeginAndRBegin&>, int*>);
-#endif // _LIBCPP_STD_VER <= 23
+#endif // _LIBCPP_STD_VER < 23
 
 constexpr bool testBeginEnd() {
   MemberBeginEnd a{};
@@ -508,7 +498,7 @@ constexpr bool testBeginEnd() {
   assert(base(std::ranges::rend(d).base()) == &d.b);
   assert(base(std::ranges::rend(dd).base()) == &dd.cb);
 
-#if _LIBCPP_STD_VER <= 23
+#if _LIBCPP_STD_VER < 23
   assert(base(std::ranges::crend(a).base()) == &a.cb);
   assert(base(std::ranges::crend(aa).base()) == &aa.cb);
   assert(base(std::ranges::crend(b).base()) == &b.cb);
@@ -517,25 +507,25 @@ constexpr bool testBeginEnd() {
   assert(base(std::ranges::crend(cc).base()) == &cc.cb);
   assert(base(std::ranges::crend(d).base()) == &d.cb);
   assert(base(std::ranges::crend(dd).base()) == &dd.cb);
-#endif // _LIBCPP_STD_VER <= 23
+#endif // _LIBCPP_STD_VER < 23
 
   return true;
 }
 
 
 ASSERT_NOEXCEPT(std::ranges::rend(std::declval<int (&)[10]>()));
-#if _LIBCPP_STD_VER <= 23
+#if _LIBCPP_STD_VER < 23
 ASSERT_NOEXCEPT(std::ranges::crend(std::declval<int (&)[10]>()));
-#endif // _LIBCPP_STD_VER <= 23
+#endif // _LIBCPP_STD_VER < 23
 
 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)));
-#if _LIBCPP_STD_VER <= 23
+#if _LIBCPP_STD_VER < 23
 static_assert(noexcept(std::ranges::crend(ntmre)));
-#endif // _LIBCPP_STD_VER <= 23
+#endif // _LIBCPP_STD_VER < 23
 
 struct NoThrowADLREnd {
   ThrowingIterator<int> rbegin() const;
@@ -543,55 +533,55 @@ struct NoThrowADLREnd {
   friend ThrowingIterator<int> rend(const NoThrowADLREnd&) noexcept;
 } ntare;
 static_assert(noexcept(std::ranges::rend(ntare)));
-#if _LIBCPP_STD_VER <= 23
+#if _LIBCPP_STD_VER < 23
 static_assert(noexcept(std::ranges::crend(ntare)));
-#endif // _LIBCPP_STD_VER <= 23
+#endif // _LIBCPP_STD_VER < 23
 
 struct NoThrowMemberREndReturnsRef {
   ThrowingIterator<int> rbegin() const;
   ThrowingIterator<int>& rend() const noexcept; // auto(t.rend()) may throw
 } ntmrerr;
 static_assert(!noexcept(std::ranges::rend(ntmrerr)));
-#if _LIBCPP_STD_VER <= 23
+#if _LIBCPP_STD_VER < 23
 static_assert(!noexcept(std::ranges::crend(ntmrerr)));
-#endif // _LIBCPP_STD_VER <= 23
+#endif // _LIBCPP_STD_VER < 23
 
 struct REndReturnsArrayRef {
     auto rbegin() const noexcept -> int(&)[10];
     auto rend() const noexcept -> int(&)[10];
 } rerar;
 static_assert(noexcept(std::ranges::rend(rerar)));
-#if _LIBCPP_STD_VER <= 23
+#if _LIBCPP_STD_VER < 23
 static_assert(noexcept(std::ranges::crend(rerar)));
-#endif // _LIBCPP_STD_VER <= 23
+#endif // _LIBCPP_STD_VER < 23
 
 struct NoThrowBeginThrowingEnd {
   int* begin() const noexcept;
   int* end() const;
 } ntbte;
 static_assert(noexcept(std::ranges::rend(ntbte)));
-#if _LIBCPP_STD_VER <= 23
+#if _LIBCPP_STD_VER < 23
 static_assert(noexcept(std::ranges::crend(ntbte)));
-#endif // _LIBCPP_STD_VER <= 23
+#endif // _LIBCPP_STD_VER < 23
 
 struct NoThrowEndThrowingBegin {
   int* begin() const;
   int* end() const noexcept;
 } ntetb;
 static_assert(!noexcept(std::ranges::rend(ntetb)));
-#if _LIBCPP_STD_VER <= 23
+#if _LIBCPP_STD_VER < 23
 static_assert(!noexcept(std::ranges::crend(ntetb)));
-#endif // _LIBCPP_STD_VER <= 23
+#endif // _LIBCPP_STD_VER < 23
 
 // 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>*&>);
-#if _LIBCPP_STD_VER <= 23
+#if _LIBCPP_STD_VER < 23
 static_assert(!std::is_invocable_v<RangeCREndT, Holder<Incomplete>*>);
 static_assert(!std::is_invocable_v<RangeCREndT, Holder<Incomplete>*&>);
-#endif // _LIBCPP_STD_VER <= 23
+#endif // _LIBCPP_STD_VER < 23
 
 int main(int, char**) {
   static_assert(testReturnTypes());

>From 9ae919448814ba87987bcff3fdb4a6460e1946c3 Mon Sep 17 00:00:00 2001
From: Nicole Mazzuca <nicole at strega-nil.co>
Date: Wed, 24 Jul 2024 21:04:09 +0200
Subject: [PATCH 4/4] Finish off the docs

additionally minor fix to formatting

still need to test...
---
 libcxx/docs/Status/Cxx2c.rst                           | 2 ++
 libcxx/docs/Status/Cxx2cPapers.csv                     | 2 +-
 libcxx/test/std/ranges/range.access/rend.pass.cpp      | 4 ++--
 libcxx/utils/generate_feature_test_macro_components.py | 3 +--
 4 files changed, 6 insertions(+), 5 deletions(-)

diff --git a/libcxx/docs/Status/Cxx2c.rst b/libcxx/docs/Status/Cxx2c.rst
index 1b8d96588c3cb..9514daf5464e7 100644
--- a/libcxx/docs/Status/Cxx2c.rst
+++ b/libcxx/docs/Status/Cxx2c.rst
@@ -44,6 +44,8 @@ Paper Status
    .. [#note-P2944R3] Implemented comparisons for ``reference_wrapper`` only.
    .. [#note-P2422R1] Libc++ keeps the ``nodiscard`` attributes as a conforming extension.
    .. [#note-P2997R1] This paper is applied as DR against C++20. (MSVC STL and libstdc++ will do the same.)
+   .. [#note-P2836R1] This paper is applies as DR against C++23 (MSVC STL and libstdc++ have done the same);
+      it is marked in progress as the feature macro is not yet defined and therefore has not been updated.
 
 .. _issues-status-cxx2c:
 
diff --git a/libcxx/docs/Status/Cxx2cPapers.csv b/libcxx/docs/Status/Cxx2cPapers.csv
index 130e4ef894f29..24270fbc8c04f 100644
--- a/libcxx/docs/Status/Cxx2cPapers.csv
+++ b/libcxx/docs/Status/Cxx2cPapers.csv
@@ -42,7 +42,7 @@
 "`P2819R2 <https://wg21.link/P2819R2>`__","LWG","Add tuple protocol to complex","Kona November 2023","|Complete|","19.0",""
 "`P2937R0 <https://wg21.link/P2937R0>`__","LWG","Freestanding: Remove ``strtok``","Kona November 2023","","",""
 "`P2833R2 <https://wg21.link/P2833R2>`__","LWG","Freestanding Library: inout expected span","Kona November 2023","","",""
-"`P2836R1 <https://wg21.link/P2836R1>`__","LWG","``std::basic_const_iterator`` should follow its underlying type's convertibility","Kona November 2023","","","|DR|"
+"`P2836R1 <https://wg21.link/P2836R1>`__","LWG","``std::basic_const_iterator`` should follow its underlying type's convertibility","Kona November 2023","|In progress| [#note-P2836R1]","","|DR|"
 "`P2264R7 <https://wg21.link/P2264R7>`__","LWG","Make ``assert()`` macro user friendly for C and C++","Kona November 2023","","",""
 "`P1673R13 <https://wg21.link/P1673R13>`__","LWG","A free function linear algebra interface based on the BLAS","Kona November 2023","","",""
 "","","","","","",""
diff --git a/libcxx/test/std/ranges/range.access/rend.pass.cpp b/libcxx/test/std/ranges/range.access/rend.pass.cpp
index 6029d7f960d88..1108109009dfc 100644
--- a/libcxx/test/std/ranges/range.access/rend.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/rend.pass.cpp
@@ -98,8 +98,8 @@ constexpr bool testArray() {
   assert(std::ranges::crend(b).base() == b);
   assert(std::ranges::crend(a).base() == a);
   assert(std::ranges::crend(c).base() == c);
-#endif // _LIBCPP_STD_VER < 23                                                                                        \
-       //
+#endif // _LIBCPP_STD_VER < 23
+
   return true;
 }
 
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index a351112471295..2fdfbf8a0de95 100755
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -1000,8 +1000,7 @@ def add_version_header(tc):
         {
             "name": "__cpp_lib_ranges_as_const",
             "values": {
-                "c++23": 202207  # P2278R4 cbegin should always return a constant iterator
-                #        202311  # DR P2836R1 std::basic_const_iterator should follow its underlying type’s convertibility
+                "c++23": 202311  # DR P2836R1 std::basic_const_iterator should follow its underlying type’s convertibility
             },
             "headers": ["ranges"],
             "unimplemented": True,



More information about the libcxx-commits mailing list