[libcxx-commits] [libcxx] [libcxx] cbegin should always return a constant iterator (PR #99915)

A. Jiang via libcxx-commits libcxx-commits at lists.llvm.org
Fri Nov 1 18:14:17 PDT 2024


https://github.com/frederick-vs-ja updated https://github.com/llvm/llvm-project/pull/99915

>From 5b9c2df7c1a88a52c3a854a192097925fc6610e4 Mon Sep 17 00:00:00 2001
From: Nicole Mazzuca <nicole at strega-nil.co>
Date: Mon, 2 Sep 2024 13:52:03 +0200
Subject: [PATCH 1/5]  [libcxx] `cbegin` should always return a constant
 iterator

Papers:
  - [P2278R4][]: `cbegin` should always return a constant iterator
  - [P2836R1][]: `std::basic_const_iterator` should follow its
    underlying type's convertibility

Issues:
  - [LWG3765][]: `const_sentinel` should be constrained
  - [LWG3766][]: `view_interface::cbegin` is underconstrained
  - [LWG3770][]: `const_sentinel_t` is missing
  - [LWG3769][]: `basic_const_iterator::operator==` causes infinite
    constraint recursion
  - [LWG3853][]: `basic_const_iterator<volatile int*>::operator->` is
    ill-formed
  - [LWG3862][]: `basic_const_iterator`'s `common_type` specialization
    is underconstrained
  - [LWG3872][]: `basic_const_iterator` should have custom `iter_move`
  - [LWG3946][]: The definition of `const_iterator_t` should be reworked
  - [LWG3948][]: _`possibly-const-range`_ and _`as-const-pointer`_
    should be `noexcept`

Implements

- `basic_const_iterator`, `const_iterator`, and `const_sentinel`
- The const accessors - `ranges::{cbegin,cend,crbegin,crend}`
- Add `cbegin` and `cend` member functions to `view_interface`
- `ranges::as_const_view`, `ranges::views::as_const`
- The changes to `span`
- `cdata`
- The changes from [P2836R1][].

Drive-by:
- Fix some includes that would have been circular.

[P2278R4]: https://wg21.link/p2278r4
[P2836R1]: https://wg21.link/p2836r1
[LWG3765]: https://wg21.link/lwg3765
[LWG3766]: https://wg21.link/lwg3766
[LWG3770]: https://wg21.link/lwg3770
[LWG3769]: https://wg21.link/lwg3769
[LWG3853]: https://wg21.link/lwg3853
[LWG3862]: https://wg21.link/lwg3862
[LWG3872]: https://wg21.link/lwg3872
[LWG3946]: https://wg21.link/lwg3946
[LWG3948]: https://wg21.link/lwg3948
---
 libcxx/docs/FeatureTestMacroTable.rst         |   2 +-
 libcxx/docs/Status/Cxx23Issues.csv            |  14 +-
 libcxx/docs/Status/Cxx23Papers.csv            |   2 +-
 libcxx/docs/Status/Cxx2cIssues.csv            |   4 +-
 libcxx/docs/Status/Cxx2cPapers.csv            |   2 +-
 libcxx/include/CMakeLists.txt                 |   4 +-
 .../include/__algorithm/ranges_reverse_copy.h |  10 +-
 .../__format/range_default_formatter.h        |   2 +-
 libcxx/include/__format/range_formatter.h     |   2 +-
 libcxx/include/__iterator/const_iterator.h    | 351 ++++++++++++++++++
 libcxx/include/__iterator/reverse_iterator.h  |  13 -
 libcxx/include/__ranges/access.h              |  62 ++--
 libcxx/include/__ranges/as_const_view.h       | 205 ++++++++++
 libcxx/include/__ranges/concepts.h            |  14 +-
 libcxx/include/__ranges/const_access.h        | 266 +++++++++++++
 libcxx/include/__ranges/data.h                | 102 -----
 libcxx/include/__ranges/owning_view.h         |   1 -
 libcxx/include/__ranges/rbegin.h              |  29 --
 libcxx/include/__ranges/ref_view.h            |   1 -
 libcxx/include/__ranges/rend.h                |  28 --
 libcxx/include/__ranges/view_interface.h      |  22 ++
 libcxx/include/iterator                       |  29 ++
 libcxx/include/module.modulemap               |   4 +-
 libcxx/include/ranges                         |  12 +-
 libcxx/include/span                           |  68 +++-
 libcxx/include/string_view                    |   2 +-
 libcxx/include/version                        |   4 +-
 libcxx/modules/std/iterator.inc               |  15 +-
 libcxx/modules/std/ranges.inc                 |  14 +-
 ...range_concept_conformance.compile.pass.cpp |   7 +
 .../views.span/span.iterators/begin.pass.cpp  | 138 +++----
 .../views.span/span.iterators/end.pass.cpp    | 141 +++----
 .../span.iterators/iterator.pass.cpp          |  17 +-
 ...rator_concept_conformance.compile.pass.cpp |  20 +
 .../views.span/span.iterators/rbegin.pass.cpp | 141 +++----
 .../views.span/span.iterators/rend.pass.cpp   | 137 +++----
 .../iterators/const.iterators/alias.pass.cpp  |  32 ++
 .../const.iterators/iterator.pass.cpp         | 139 +++++++
 .../ranges.version.compile.pass.cpp           |  34 +-
 .../version.version.compile.pass.cpp          |  34 +-
 .../std/ranges/range.access/begin.pass.cpp    | 160 ++++----
 .../std/ranges/range.access/cbegin.pass.cpp   | 207 +++++++++++
 .../std/ranges/range.access/cbegin.verify.cpp |  25 ++
 .../std/ranges/range.access/cend.pass.cpp     | 207 +++++++++++
 .../std/ranges/range.access/cend.verify.cpp   |  25 ++
 .../std/ranges/range.access/data.pass.cpp     | 126 ++++++-
 .../test/std/ranges/range.access/end.pass.cpp | 159 ++++----
 .../std/ranges/range.access/rbegin.pass.cpp   | 233 +++++++-----
 .../std/ranges/range.access/rend.pass.cpp     | 227 ++++++-----
 .../range.as.const/adaptor.pass.cpp           | 156 ++++++++
 .../range.as.const/base.pass.cpp              |  78 ++++
 .../range.as.const/begin.pass.cpp             | 124 +++++++
 .../range.as.const/ctad.compile.pass.cpp      |  22 ++
 .../range.as.const/ctor.pass.cpp              |  57 +++
 .../enable_borrowed_range.compile.pass.cpp    |  16 +
 .../range.as.const/end.pass.cpp               | 129 +++++++
 .../range.as.const/size.pass.cpp              |  80 ++++
 .../view.interface/view.interface.pass.cpp    |  22 +-
 libcxx/test/support/test_iterators.h          |   7 +
 libcxx/test/support/test_macros.h             |   6 +
 .../generate_feature_test_macro_components.py |   4 +-
 61 files changed, 3248 insertions(+), 946 deletions(-)
 create mode 100644 libcxx/include/__iterator/const_iterator.h
 create mode 100644 libcxx/include/__ranges/as_const_view.h
 create mode 100644 libcxx/include/__ranges/const_access.h
 delete mode 100644 libcxx/include/__ranges/data.h
 create mode 100644 libcxx/test/std/iterators/const.iterators/alias.pass.cpp
 create mode 100644 libcxx/test/std/iterators/const.iterators/iterator.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.access/cbegin.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.access/cbegin.verify.cpp
 create mode 100644 libcxx/test/std/ranges/range.access/cend.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.access/cend.verify.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.as.const/adaptor.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.as.const/base.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.as.const/begin.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.as.const/ctad.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.as.const/ctor.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.as.const/enable_borrowed_range.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.as.const/end.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.as.const/size.pass.cpp

diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index c909a4300db1a6..bc4ffa3d71c66a 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -352,7 +352,7 @@ Status
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_ranges``                                       ``202406L``
     ---------------------------------------------------------- -----------------
-    ``__cpp_lib_ranges_as_const``                              *unimplemented*
+    ``__cpp_lib_ranges_as_const``                              ``202311L``
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_ranges_as_rvalue``                             ``202207L``
     ---------------------------------------------------------- -----------------
diff --git a/libcxx/docs/Status/Cxx23Issues.csv b/libcxx/docs/Status/Cxx23Issues.csv
index da4cce6eac0c90..6c4deb4d4ad9df 100644
--- a/libcxx/docs/Status/Cxx23Issues.csv
+++ b/libcxx/docs/Status/Cxx23Issues.csv
@@ -219,9 +219,9 @@
 "`LWG3761 <https://wg21.link/LWG3761>`__","``cartesian_product_view::iterator::operator-`` should pass by reference","2022-11 (Kona)","","",""
 "`LWG3762 <https://wg21.link/LWG3762>`__","``generator::iterator::operator==`` should pass by reference","2022-11 (Kona)","","",""
 "`LWG3764 <https://wg21.link/LWG3764>`__","``reference_wrapper::operator()`` should propagate noexcept","2022-11 (Kona)","|Complete|","17.0",""
-"`LWG3765 <https://wg21.link/LWG3765>`__","``const_sentinel`` should be constrained","2022-11 (Kona)","","",""
-"`LWG3766 <https://wg21.link/LWG3766>`__","``view_interface::cbegin`` is underconstrained","2022-11 (Kona)","","",""
-"`LWG3770 <https://wg21.link/LWG3770>`__","``const_sentinel_t`` is missing","2022-11 (Kona)","","",""
+"`LWG3765 <https://wg21.link/LWG3765>`__","``const_sentinel`` should be constrained","2022-11 (Kona)","|Complete|","20.0",""
+"`LWG3766 <https://wg21.link/LWG3766>`__","``view_interface::cbegin`` is underconstrained","2022-11 (Kona)","|Complete|","20.0",""
+"`LWG3770 <https://wg21.link/LWG3770>`__","``const_sentinel_t`` is missing","2022-11 (Kona)","|Complete|","20.0",""
 "`LWG3773 <https://wg21.link/LWG3773>`__","``views::zip_transform`` still requires ``F`` to be ``copy_constructible`` when empty pack","2022-11 (Kona)","","",""
 "`LWG3774 <https://wg21.link/LWG3774>`__","``<flat_set>`` should include ``<compare>``","2022-11 (Kona)","","",""
 "`LWG3775 <https://wg21.link/LWG3775>`__","Broken dependencies in the ``Cpp17Allocator`` requirements","2022-11 (Kona)","","",""
@@ -252,7 +252,7 @@
 "`LWG3664 <https://wg21.link/LWG3664>`__","`LWG 3392 <https://wg21.link/LWG3392>`__ ``broke std::ranges::distance(a, a+3)``","2023-02 (Issaquah)","","",""
 "`LWG3720 <https://wg21.link/LWG3720>`__","Restrict the valid types of ``arg-id`` for width and precision in ``std-format-spec``","2023-02 (Issaquah)","|Complete|","17.0",""
 "`LWG3756 <https://wg21.link/LWG3756>`__","Is the ``std::atomic_flag`` class signal-safe?","2023-02 (Issaquah)","","",""
-"`LWG3769 <https://wg21.link/LWG3769>`__","``basic_const_iterator::operator==`` causes infinite constraint recursion","2023-02 (Issaquah)","","",""
+"`LWG3769 <https://wg21.link/LWG3769>`__","``basic_const_iterator::operator==`` causes infinite constraint recursion","2023-02 (Issaquah)","|Complete|","20.0",""
 "`LWG3807 <https://wg21.link/LWG3807>`__","The feature test macro for ``ranges::find_last`` should be renamed","2023-02 (Issaquah)","","",""
 "`LWG3811 <https://wg21.link/LWG3811>`__","``views::as_const`` on ``ref_view<T>`` should return ``ref_view<const T>``","2023-02 (Issaquah)","","",""
 "`LWG3820 <https://wg21.link/LWG3820>`__","``cartesian_product_view::iterator::prev`` is not quite right","2023-02 (Issaquah)","","",""
@@ -271,7 +271,7 @@
 "`LWG3849 <https://wg21.link/LWG3849>`__","``cartesian_product_view::iterator``'s default constructor is overconstrained","2023-02 (Issaquah)","","",""
 "`LWG3850 <https://wg21.link/LWG3850>`__","``views::as_const`` on ``empty_view<T>`` should return ``empty_view<const T>``","2023-02 (Issaquah)","","",""
 "`LWG3851 <https://wg21.link/LWG3851>`__","``chunk_view::inner-iterator`` missing custom ``iter_move`` and ``iter_swap``","2023-02 (Issaquah)","","",""
-"`LWG3853 <https://wg21.link/LWG3853>`__","``basic_const_iterator<volatile int*>::operator->`` is ill-formed","2023-02 (Issaquah)","","",""
+"`LWG3853 <https://wg21.link/LWG3853>`__","``basic_const_iterator<volatile int*>::operator->`` is ill-formed","2023-02 (Issaquah)","|Complete|","20.0",""
 "`LWG3857 <https://wg21.link/LWG3857>`__","``basic_string_view`` should allow explicit conversion when only traits vary","2023-02 (Issaquah)","|Complete|","17.0",""
 "`LWG3860 <https://wg21.link/LWG3860>`__","``range_common_reference_t`` is missing","2023-02 (Issaquah)","|Complete|","17.0",""
 "`LWG3866 <https://wg21.link/LWG3866>`__","Bad Mandates for ``expected::transform_error`` overloads","2023-02 (Issaquah)","|Complete|","17.0",""
@@ -293,12 +293,12 @@
 "`LWG3836 <https://wg21.link/LWG3836>`__","``std::expected<bool, E1>`` conversion constructor ``expected(const expected<U, G>&)`` should take precedence over ``expected(U&&)`` with operator ``bool``","2023-02 (Issaquah)","|Complete|","18.0",""
 "`LWG3843 <https://wg21.link/LWG3843>`__","``std::expected<T,E>::value() &`` assumes ``E`` is copy constructible","2023-02 (Issaquah)","|Complete|","17.0",""
 "`LWG3847 <https://wg21.link/LWG3847>`__","``ranges::to`` can still return views","2023-02 (Issaquah)","|Complete|","17.0",""
-"`LWG3862 <https://wg21.link/LWG3862>`__","``basic_const_iterator``'s ``common_type`` specialization is underconstrained","2023-02 (Issaquah)","","",""
+"`LWG3862 <https://wg21.link/LWG3862>`__","``basic_const_iterator``'s ``common_type`` specialization is underconstrained","2023-02 (Issaquah)","|Complete|","20.0",""
 "`LWG3865 <https://wg21.link/LWG3865>`__","Sorting a range of ``pairs``","2023-02 (Issaquah)","|Complete|","17.0",""
 "`LWG3869 <https://wg21.link/LWG3869>`__","Deprecate ``std::errc`` constants related to UNIX STREAMS","2023-02 (Issaquah)","|Complete|","19.0",""
 "`LWG3870 <https://wg21.link/LWG3870>`__","Remove ``voidify``","2023-02 (Issaquah)","","",""
 "`LWG3871 <https://wg21.link/LWG3871>`__","Adjust note about ``terminate``","2023-02 (Issaquah)","","",""
-"`LWG3872 <https://wg21.link/LWG3872>`__","``basic_const_iterator`` should have custom ``iter_move``","2023-02 (Issaquah)","","",""
+"`LWG3872 <https://wg21.link/LWG3872>`__","``basic_const_iterator`` should have custom ``iter_move``","2023-02 (Issaquah)","|Complete|","20.0",""
 "`LWG3875 <https://wg21.link/LWG3875>`__","``std::ranges::repeat_view<T, IntegerClass>::iterator`` may be ill-formed","2023-02 (Issaquah)","|Complete|","17.0",""
 "`LWG3876 <https://wg21.link/LWG3876>`__","Default constructor of ``std::layout_XX::mapping`` misses precondition","2023-02 (Issaquah)","","",""
 "`LWG3877 <https://wg21.link/LWG3877>`__","Incorrect constraints on ``const``-qualified monadic overloads for ``std::expected``","2023-02 (Issaquah)","|Complete|","17.0",""
diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index 2c8a91d8401b53..4547d0ec5a10ad 100644
--- a/libcxx/docs/Status/Cxx23Papers.csv
+++ b/libcxx/docs/Status/Cxx23Papers.csv
@@ -61,7 +61,7 @@
 "`P1899R3 <https://wg21.link/P1899R3>`__","``stride_view``","2022-07 (Virtual)","","",""
 "`P2093R14 <https://wg21.link/P2093R14>`__","Formatted output","2022-07 (Virtual)","|Complete|","18.0",""
 "`P2165R4 <https://wg21.link/P2165R4>`__","Compatibility between ``tuple``, ``pair`` and ``tuple-like`` objects","2022-07 (Virtual)","","",""
-"`P2278R4 <https://wg21.link/P2278R4>`__","``cbegin`` should always return a constant iterator","2022-07 (Virtual)","","",""
+"`P2278R4 <https://wg21.link/P2278R4>`__","``cbegin`` should always return a constant iterator","2022-07 (Virtual)","|Complete|","20.0",""
 "`P2286R8 <https://wg21.link/P2286R8>`__","Formatting Ranges","2022-07 (Virtual)","|Complete|","16.0",""
 "`P2291R3 <https://wg21.link/P2291R3>`__","Add Constexpr Modifiers to Functions ``to_chars`` and ``from_chars`` for Integral Types in ``<charconv>`` Header","2022-07 (Virtual)","|Complete|","16.0",""
 "`P2302R4 <https://wg21.link/P2302R4>`__","``std::ranges::contains``","2022-07 (Virtual)","|Complete|","19.0",""
diff --git a/libcxx/docs/Status/Cxx2cIssues.csv b/libcxx/docs/Status/Cxx2cIssues.csv
index 14633265a42251..bcb046bd6d2801 100644
--- a/libcxx/docs/Status/Cxx2cIssues.csv
+++ b/libcxx/docs/Status/Cxx2cIssues.csv
@@ -25,9 +25,9 @@
 "`LWG3809 <https://wg21.link/LWG3809>`__","Is ``std::subtract_with_carry_engine<uint16_t>`` supposed to work","2023-11 (Kona)","","",""
 "`LWG3892 <https://wg21.link/LWG3892>`__","Incorrect formatting of nested ranges and tuples","2023-11 (Kona)","|Complete|","17.0",""
 "`LWG3897 <https://wg21.link/LWG3897>`__","``inout_ptr`` will not update raw pointer to 0","2023-11 (Kona)","|Complete|","19.0",""
-"`LWG3946 <https://wg21.link/LWG3946>`__","The definition of ``const_iterator_t`` should be reworked","2023-11 (Kona)","","",""
+"`LWG3946 <https://wg21.link/LWG3946>`__","The definition of ``const_iterator_t`` should be reworked","2023-11 (Kona)","|Complete|","20.0",""
 "`LWG3947 <https://wg21.link/LWG3947>`__","Unexpected constraints on ``adjacent_transform_view::base()``","2023-11 (Kona)","","",""
-"`LWG3948 <https://wg21.link/LWG3948>`__","``possibly-const-range and as-const-pointer`` should be ``noexcept``","2023-11 (Kona)","","",""
+"`LWG3948 <https://wg21.link/LWG3948>`__","``possibly-const-range and as-const-pointer`` should be ``noexcept``","2023-11 (Kona)","|Complete|","20.0",""
 "`LWG3949 <https://wg21.link/LWG3949>`__","``std::atomic<bool>``'s trivial destructor dropped in C++17 spec wording","2023-11 (Kona)","","",""
 "`LWG3951 <https://wg21.link/LWG3951>`__","[expected.object.swap]: Using ``value()`` instead of ``has_value()``","2023-11 (Kona)","|Complete|","16.0",""
 "`LWG3953 <https://wg21.link/LWG3953>`__","``iter_move`` for ``common_iterator`` and ``counted_iterator`` should return ``decltype(auto)``","2023-11 (Kona)","","",""
diff --git a/libcxx/docs/Status/Cxx2cPapers.csv b/libcxx/docs/Status/Cxx2cPapers.csv
index 8864b1ebe28891..328586e1a027c9 100644
--- a/libcxx/docs/Status/Cxx2cPapers.csv
+++ b/libcxx/docs/Status/Cxx2cPapers.csv
@@ -42,7 +42,7 @@
 "`P2819R2 <https://wg21.link/P2819R2>`__","Add tuple protocol to complex","2023-11 (Kona)","|Complete|","19.0",""
 "`P2937R0 <https://wg21.link/P2937R0>`__","Freestanding: Remove ``strtok``","2023-11 (Kona)","","",""
 "`P2833R2 <https://wg21.link/P2833R2>`__","Freestanding Library: inout expected span","2023-11 (Kona)","","",""
-"`P2836R1 <https://wg21.link/P2836R1>`__","``std::basic_const_iterator`` should follow its underlying type's convertibility","2023-11 (Kona)","","",""
+"`P2836R1 <https://wg21.link/P2836R1>`__","``std::basic_const_iterator`` should follow its underlying type's convertibility","2023-11 (Kona)","|Complete|","20.0","Implemented as a DR against C++23. (MSVC STL and libstdc++ will do the same.)"
 "`P2264R7 <https://wg21.link/P2264R7>`__","Make ``assert()`` macro user friendly for C and C++","2023-11 (Kona)","","",""
 "`P1673R13 <https://wg21.link/P1673R13>`__","A free function linear algebra interface based on the BLAS","2023-11 (Kona)","","",""
 "","","","","",""
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 32579272858a8e..99d8b22281a777 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
@@ -635,14 +636,15 @@ set(files
   __random/weibull_distribution.h
   __ranges/access.h
   __ranges/all.h
+  __ranges/as_const_view.h
   __ranges/as_rvalue_view.h
   __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
-  __ranges/data.h
   __ranges/drop_view.h
   __ranges/drop_while_view.h
   __ranges/elements_view.h
diff --git a/libcxx/include/__algorithm/ranges_reverse_copy.h b/libcxx/include/__algorithm/ranges_reverse_copy.h
index e5ca5cf652dc4d..c290fd24946fbe 100644
--- a/libcxx/include/__algorithm/ranges_reverse_copy.h
+++ b/libcxx/include/__algorithm/ranges_reverse_copy.h
@@ -34,6 +34,14 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 namespace ranges {
 
+template <ranges::bidirectional_range _Range>
+_LIBCPP_HIDE_FROM_ABI constexpr ranges::subrange<reverse_iterator<ranges::iterator_t<_Range>>,
+                                                 reverse_iterator<ranges::iterator_t<_Range>>>
+__reverse_range(_Range&& __range) {
+  auto __first = ranges::begin(__range);
+  return {std::make_reverse_iterator(ranges::next(__first, ranges::end(__range))), std::make_reverse_iterator(__first)};
+}
+
 template <class _InIter, class _OutIter>
 using reverse_copy_result = in_out_result<_InIter, _OutIter>;
 
@@ -49,7 +57,7 @@ struct __reverse_copy {
     requires indirectly_copyable<iterator_t<_Range>, _OutIter>
   _LIBCPP_HIDE_FROM_ABI constexpr reverse_copy_result<borrowed_iterator_t<_Range>, _OutIter>
   operator()(_Range&& __range, _OutIter __result) const {
-    auto __ret = ranges::copy(std::__reverse_range(__range), std::move(__result));
+    auto __ret = ranges::copy(ranges::__reverse_range(__range), std::move(__result));
     return {ranges::next(ranges::begin(__range), ranges::end(__range)), std::move(__ret.out)};
   }
 };
diff --git a/libcxx/include/__format/range_default_formatter.h b/libcxx/include/__format/range_default_formatter.h
index fb21b0f8beb3a1..c0515892b12be9 100644
--- a/libcxx/include/__format/range_default_formatter.h
+++ b/libcxx/include/__format/range_default_formatter.h
@@ -22,8 +22,8 @@
 #include <__format/formatter.h>
 #include <__format/range_formatter.h>
 #include <__iterator/back_insert_iterator.h>
+#include <__ranges/access.h>
 #include <__ranges/concepts.h>
-#include <__ranges/data.h>
 #include <__ranges/from_range.h>
 #include <__ranges/size.h>
 #include <__type_traits/conditional.h>
diff --git a/libcxx/include/__format/range_formatter.h b/libcxx/include/__format/range_formatter.h
index def55c86ce51cd..c7a07727dee2ef 100644
--- a/libcxx/include/__format/range_formatter.h
+++ b/libcxx/include/__format/range_formatter.h
@@ -26,8 +26,8 @@
 #include <__format/formatter_output.h>
 #include <__format/parser_std_format_spec.h>
 #include <__iterator/back_insert_iterator.h>
+#include <__ranges/access.h>
 #include <__ranges/concepts.h>
-#include <__ranges/data.h>
 #include <__ranges/from_range.h>
 #include <__ranges/size.h>
 #include <__type_traits/remove_cvref.h>
diff --git a/libcxx/include/__iterator/const_iterator.h b/libcxx/include/__iterator/const_iterator.h
new file mode 100644
index 00000000000000..4fcd76b4117dc6
--- /dev/null
+++ b/libcxx/include/__iterator/const_iterator.h
@@ -0,0 +1,351 @@
+// -*- 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/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
+#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>>;
+
+// This doesn't use `conditional_t` to avoid instantiating const_iterator<_Sent> when _Sent is not an input_iterator.
+template <class _Sent>
+struct __const_sentinel_impl {
+  using type = _Sent;
+};
+template <class _Sent>
+  requires input_iterator<_Sent>
+struct __const_sentinel_impl<_Sent> {
+  using type = const_iterator<_Sent>;
+};
+template <semiregular _Sent>
+using const_sentinel = __const_sentinel_impl<_Sent>::type;
+
+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 = std::iterator_traits<_Iter>::iterator_category;
+};
+
+template <input_iterator _Iter>
+class _LIBCPP_TEMPLATE_VIS basic_const_iterator : public __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_); }
+
+  _LIBCPP_HIDE_FROM_ABI 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;
+  }
+  _LIBCPP_HIDE_FROM_ABI 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;
+  }
+  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;
+  }
+  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;
+  }
+  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;
+  }
+  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;
+  }
+
+  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/__iterator/reverse_iterator.h b/libcxx/include/__iterator/reverse_iterator.h
index 50c0f21eaa286b..823203cadf7e18 100644
--- a/libcxx/include/__iterator/reverse_iterator.h
+++ b/libcxx/include/__iterator/reverse_iterator.h
@@ -27,9 +27,6 @@
 #include <__iterator/readable_traits.h>
 #include <__iterator/segmented_iterator.h>
 #include <__memory/addressof.h>
-#include <__ranges/access.h>
-#include <__ranges/concepts.h>
-#include <__ranges/subrange.h>
 #include <__type_traits/conditional.h>
 #include <__type_traits/enable_if.h>
 #include <__type_traits/is_assignable.h>
@@ -315,16 +312,6 @@ inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 reverse_iterator<_Ite
 }
 #endif
 
-#if _LIBCPP_STD_VER >= 20
-template <ranges::bidirectional_range _Range>
-_LIBCPP_HIDE_FROM_ABI constexpr ranges::subrange<reverse_iterator<ranges::iterator_t<_Range>>,
-                                                 reverse_iterator<ranges::iterator_t<_Range>>>
-__reverse_range(_Range&& __range) {
-  auto __first = ranges::begin(__range);
-  return {std::make_reverse_iterator(ranges::next(__first, ranges::end(__range))), std::make_reverse_iterator(__first)};
-}
-#endif
-
 template <class _Iter, bool __b>
 struct __unwrap_iter_impl<reverse_iterator<reverse_iterator<_Iter> >, __b> {
   using _UnwrappedIter  = decltype(__unwrap_iter_impl<_Iter>::__unwrap(std::declval<_Iter>()));
diff --git a/libcxx/include/__ranges/access.h b/libcxx/include/__ranges/access.h
index c0a40c5e10178a..251898128f078d 100644
--- a/libcxx/include/__ranges/access.h
+++ b/libcxx/include/__ranges/access.h
@@ -14,10 +14,13 @@
 #include <__config>
 #include <__iterator/concepts.h>
 #include <__iterator/readable_traits.h>
+#include <__memory/pointer_traits.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__type_traits/decay.h>
+#include <__type_traits/is_pointer.h>
 #include <__type_traits/is_reference.h>
 #include <__type_traits/remove_cvref.h>
+#include <__type_traits/remove_pointer.h>
 #include <__type_traits/remove_reference.h>
 #include <__utility/auto_cast.h>
 #include <__utility/declval.h>
@@ -148,58 +151,39 @@ inline constexpr auto end = __end::__fn{};
 } // namespace __cpo
 } // namespace ranges
 
-// [range.access.cbegin]
+// [range.prim.data]
 
 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));
-  }
+namespace __data {
+template <class _Tp>
+concept __ptr_to_object = is_pointer_v<_Tp> && is_object_v<remove_pointer_t<_Tp>>;
 
-  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));
-  }
+template <class _Tp>
+concept __member_data = __can_borrow<_Tp> && requires(_Tp&& __t) {
+  { _LIBCPP_AUTO_CAST(__t.data()) } -> __ptr_to_object;
 };
-} // namespace __cbegin
 
-inline namespace __cpo {
-inline constexpr auto cbegin = __cbegin::__fn{};
-} // namespace __cpo
-} // namespace ranges
-
-// [range.access.cend]
+template <class _Tp>
+concept __ranges_begin_invocable = !__member_data<_Tp> && __can_borrow<_Tp> && requires(_Tp&& __t) {
+  { ranges::begin(__t) } -> contiguous_iterator;
+};
 
-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 <__member_data _Tp>
+  _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const noexcept(noexcept(__t.data())) {
+    return __t.data();
   }
 
-  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));
+  template <__ranges_begin_invocable _Tp>
+  _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const
+      noexcept(noexcept(std::to_address(ranges::begin(__t)))) {
+    return std::to_address(ranges::begin(__t));
   }
 };
-} // namespace __cend
+} // namespace __data
 
 inline namespace __cpo {
-inline constexpr auto cend = __cend::__fn{};
+inline constexpr auto data = __data::__fn{};
 } // namespace __cpo
 } // namespace ranges
 
diff --git a/libcxx/include/__ranges/as_const_view.h b/libcxx/include/__ranges/as_const_view.h
new file mode 100644
index 00000000000000..d2613ad939fd0d
--- /dev/null
+++ b/libcxx/include/__ranges/as_const_view.h
@@ -0,0 +1,205 @@
+// -*- 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_AS_CONST_VIEW_H
+#define _LIBCPP___RANGES_AS_CONST_VIEW_H
+
+#include <__concepts/constructible.h>
+#include <__iterator/concepts.h>
+#include <__ranges/all.h>
+#include <__ranges/concepts.h>
+#include <__ranges/const_access.h>
+#include <__ranges/empty_view.h>
+#include <__ranges/range_adaptor.h>
+#include <__ranges/ref_view.h>
+#include <__ranges/size.h>
+#include <__ranges/view_interface.h>
+#include <__type_traits/is_specialization.h>
+#include <__utility/auto_cast.h>
+#include <__utility/move.h>
+#include <__utility/pair.h>
+#include <cstddef>
+#include <span>
+
+#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
+
+namespace ranges {
+template <input_range _View>
+  requires view<_View>
+class as_const_view : public view_interface<as_const_view<_View>> {
+  _LIBCPP_NO_UNIQUE_ADDRESS _View __base_ = _View();
+
+public:
+  _LIBCPP_HIDE_FROM_ABI as_const_view()
+    requires default_initializable<_View>
+  = default;
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit as_const_view(_View __base) : __base_(std::move(__base)) {}
+
+  _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
+    requires copy_constructible<_View>
+  {
+    return __base_;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr auto begin()
+    requires(!__simple_view<_View>)
+  {
+    return ranges::cbegin(__base_);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
+    requires range<const _View>
+  {
+    return ranges::cbegin(__base_);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr auto end()
+    requires(!__simple_view<_View>)
+  {
+    return ranges::cend(__base_);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
+    requires range<const _View>
+  {
+    return ranges::cend(__base_);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr auto size()
+    requires sized_range<_View>
+  {
+    return ranges::size(__base_);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
+    requires sized_range<const _View>
+  {
+    return ranges::size(__base_);
+  }
+};
+
+template <class _Range>
+as_const_view(_Range&&) -> as_const_view<views::all_t<_Range>>;
+
+template <class _Tp>
+inline constexpr bool enable_borrowed_range<as_const_view<_Tp>> = enable_borrowed_range<_Tp>;
+
+namespace views {
+namespace __as_const {
+
+template <class _Tp>
+inline constexpr bool __is_span_v = false; // true if and only if _Tp is a specialization of span
+template <class _Tp, size_t _Extent>
+inline constexpr bool __is_span_v<span<_Tp, _Extent>> = true;
+
+template <class _UType>
+struct __xtype {
+  using type = void;
+};
+template <class _XType>
+struct __xtype<empty_view<_XType>> {
+  using type = _XType;
+};
+template <class _XType, size_t _Extent>
+struct __xtype<span<_XType, _Extent>> {
+  using type                       = _XType;
+  constexpr static size_t __extent = _Extent;
+};
+template <class _XType>
+struct __xtype<ref_view<_XType>> {
+  using type = _XType;
+};
+
+struct __fn : __range_adaptor_closure<__fn> {
+  // implementation strategy taken from Microsoft's STL
+  enum class __strategy {
+    __already_const,
+    __empty_view,
+    __span,
+    __ref_view,
+    __const_is_constant_range,
+    __otherwise,
+    __none,
+  };
+
+  template <class _Type>
+  static consteval pair<__strategy, bool> __choose_strategy() {
+    using _UType = std::remove_cvref_t<_Type>;
+    using _XType = __xtype<_UType>::type;
+
+    if constexpr (!requires { typename all_t<_Type>; }) {
+      return {__strategy::__none, false};
+    } else if constexpr (constant_range<all_t<_Type>>) {
+      return {__strategy::__already_const, noexcept(views::all(std::declval<_Type>()))};
+    } else if constexpr (__is_specialization_v<_UType, empty_view>) {
+      return {__strategy::__empty_view, true};
+    } else if constexpr (__is_span_v<_UType>) {
+      return {__strategy::__span, true};
+    } else if constexpr (__is_specialization_v<_UType, ref_view> && constant_range<const _XType>) {
+      return {__strategy::__ref_view, noexcept(ref_view(static_cast<const _XType&>(std::declval<_Type>().base())))};
+    } else if constexpr (is_lvalue_reference_v<_Type> && constant_range<const _UType> && !view<_UType>) {
+      return {__strategy::__const_is_constant_range,
+              noexcept(ref_view(static_cast<const _UType&>(std::declval<_Type>())))};
+    } else {
+      return {__strategy::__otherwise, noexcept(as_const_view(std::declval<_Type>()))};
+    }
+  }
+
+  template <class _Type>
+    requires(__choose_strategy<_Type>().first != __strategy::__none)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr static auto
+  operator()(_Type&& __range) noexcept(__choose_strategy<_Type>().second) {
+    using _UType = std::remove_cvref_t<_Type>;
+    using _XType = __xtype<_UType>::type;
+
+    constexpr auto __st = __choose_strategy<_Type>().first;
+
+    if constexpr (__st == __strategy::__already_const) {
+      return views::all(std::forward<_Type>(__range));
+    } else if constexpr (__st == __strategy::__empty_view) {
+      return auto(views::empty<const _XType>);
+    } else if constexpr (__st == __strategy::__span) {
+      return span<const _XType, __xtype<_UType>::__extent>(std::forward<_Type>(__range));
+    } else if constexpr (__st == __strategy::__ref_view) {
+      return ref_view(static_cast<const _XType&>(std::forward<_Type>(__range).base()));
+    } else if constexpr (__st == __strategy::__const_is_constant_range) {
+      return ref_view(static_cast<const _UType&>(std::forward<_Type>(__range)));
+    } else if constexpr (__st == __strategy::__otherwise) {
+      return as_const_view(std::forward<_Type>(__range));
+    }
+  }
+};
+
+} // namespace __as_const
+
+inline namespace __cpo {
+inline constexpr auto as_const = __as_const::__fn{};
+} // namespace __cpo
+} // namespace views
+
+} // namespace ranges
+
+#endif // _LIBCPP_STD_VER >= 23
+
+_LIBCPP_END_NAMESPACE_STD
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___RANGES_AS_CONST_VIEW_H
diff --git a/libcxx/include/__ranges/concepts.h b/libcxx/include/__ranges/concepts.h
index 674a3f359ff99c..22ff67a73bce1c 100644
--- a/libcxx/include/__ranges/concepts.h
+++ b/libcxx/include/__ranges/concepts.h
@@ -15,12 +15,12 @@
 #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>
 #include <__iterator/readable_traits.h>
 #include <__ranges/access.h>
-#include <__ranges/data.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/enable_view.h>
 #include <__ranges/size.h>
@@ -61,6 +61,8 @@ concept borrowed_range =
 template <range _Rp>
 using sentinel_t = decltype(ranges::end(std::declval<_Rp&>()));
 
+// `const_iterator_t` and `const_sentinel_t` defined in <__ranges/const_access.h>
+
 template <range _Rp>
 using range_difference_t = iter_difference_t<iterator_t<_Rp>>;
 
@@ -70,6 +72,11 @@ using range_value_t = iter_value_t<iterator_t<_Rp>>;
 template <range _Rp>
 using range_reference_t = iter_reference_t<iterator_t<_Rp>>;
 
+#  if _LIBCPP_STD_VER >= 23
+template <range _Rp>
+using range_const_reference_t = iter_const_reference_t<iterator_t<_Rp>>;
+#  endif
+
 template <range _Rp>
 using range_rvalue_reference_t = iter_rvalue_reference_t<iterator_t<_Rp>>;
 
@@ -133,6 +140,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 00000000000000..e7b95b26529538
--- /dev/null
+++ b/libcxx/include/__ranges/const_access.h
@@ -0,0 +1,266 @@
+// -*- 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_cv.h>
+#include <__type_traits/remove_reference.h>
+#include <__utility/declval.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>
+_LIBCPP_HIDE_FROM_ABI constexpr auto& __possibly_const_range(_Rp& __rng) noexcept {
+  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>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr static auto
+  operator()(_Rng&& __rng) noexcept(noexcept(const_iterator<_UType<_Rng>>(
+      ranges::begin(ranges::__possibly_const_range(__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
+
+#  if _LIBCPP_STD_VER >= 23
+// [range.range]
+namespace ranges {
+template <class _Rp>
+using const_iterator_t = decltype(ranges::cbegin(std::declval<_Rp&>()));
+} // namespace ranges
+#  endif // _LIBCPP_STD_VER >= 23
+
+// [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>
+  [[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)));
+  }
+#  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
+
+#  if _LIBCPP_STD_VER >= 23
+// [range.range]
+namespace ranges {
+template <class _Rp>
+using const_sentinel_t = decltype(ranges::cend(std::declval<_Rp&>()));
+} // namespace ranges
+#  endif
+
+// [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>
+  [[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)));
+  }
+#  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>
+  [[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)));
+  }
+#  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
+
+// [range.prim.cdata]
+
+namespace ranges {
+namespace __cdata {
+struct __fn {
+#  if _LIBCPP_STD_VER >= 23
+  template <class _Tp>
+  _LIBCPP_HIDE_FROM_ABI constexpr static auto __as_const_pointer(const _Tp* __ptr) noexcept {
+    return __ptr;
+  }
+
+  template <__const_accessible_range _Rng>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr static auto
+  operator()(_Rng&& __rng) noexcept(noexcept(__as_const_pointer(ranges::data(ranges::__possibly_const_range(__rng)))))
+      -> decltype(__as_const_pointer(ranges::data(ranges::__possibly_const_range(__rng)))) {
+    return __as_const_pointer(ranges::data(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::data(static_cast<const remove_reference_t<_Tp>&>(__t))))
+          -> decltype(ranges::data(static_cast<const remove_reference_t<_Tp>&>(__t))) {
+    return ranges::data(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::data(static_cast<const _Tp&&>(__t)))) -> decltype(ranges::data(static_cast<const _Tp&&>(__t))) {
+    return ranges::data(static_cast<const _Tp&&>(__t));
+  }
+#  endif // ^^^ _LIBCPP_STD_VER < 23
+};
+} // namespace __cdata
+
+inline namespace __cpo {
+inline constexpr auto cdata = __cdata::__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/data.h b/libcxx/include/__ranges/data.h
deleted file mode 100644
index 50db3cffeeed8a..00000000000000
--- a/libcxx/include/__ranges/data.h
+++ /dev/null
@@ -1,102 +0,0 @@
-// -*- 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_DATA_H
-#define _LIBCPP___RANGES_DATA_H
-
-#include <__concepts/class_or_enum.h>
-#include <__config>
-#include <__iterator/concepts.h>
-#include <__iterator/iterator_traits.h>
-#include <__memory/pointer_traits.h>
-#include <__ranges/access.h>
-#include <__type_traits/decay.h>
-#include <__type_traits/is_object.h>
-#include <__type_traits/is_pointer.h>
-#include <__type_traits/is_reference.h>
-#include <__type_traits/remove_pointer.h>
-#include <__type_traits/remove_reference.h>
-#include <__utility/auto_cast.h>
-
-#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
-#  pragma GCC system_header
-#endif
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-
-#if _LIBCPP_STD_VER >= 20
-
-// [range.prim.data]
-
-namespace ranges {
-namespace __data {
-template <class _Tp>
-concept __ptr_to_object = is_pointer_v<_Tp> && is_object_v<remove_pointer_t<_Tp>>;
-
-template <class _Tp>
-concept __member_data = __can_borrow<_Tp> && requires(_Tp&& __t) {
-  { _LIBCPP_AUTO_CAST(__t.data()) } -> __ptr_to_object;
-};
-
-template <class _Tp>
-concept __ranges_begin_invocable = !__member_data<_Tp> && __can_borrow<_Tp> && requires(_Tp&& __t) {
-  { ranges::begin(__t) } -> contiguous_iterator;
-};
-
-struct __fn {
-  template <__member_data _Tp>
-  _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const noexcept(noexcept(__t.data())) {
-    return __t.data();
-  }
-
-  template <__ranges_begin_invocable _Tp>
-  _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const
-      noexcept(noexcept(std::to_address(ranges::begin(__t)))) {
-    return std::to_address(ranges::begin(__t));
-  }
-};
-} // namespace __data
-
-inline namespace __cpo {
-inline constexpr auto data = __data::__fn{};
-} // namespace __cpo
-} // namespace ranges
-
-// [range.prim.cdata]
-
-namespace ranges {
-namespace __cdata {
-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::data(static_cast<const remove_reference_t<_Tp>&>(__t))))
-          -> decltype(ranges::data(static_cast<const remove_reference_t<_Tp>&>(__t))) {
-    return ranges::data(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::data(static_cast<const _Tp&&>(__t)))) -> decltype(ranges::data(static_cast<const _Tp&&>(__t))) {
-    return ranges::data(static_cast<const _Tp&&>(__t));
-  }
-};
-} // namespace __cdata
-
-inline namespace __cpo {
-inline constexpr auto cdata = __cdata::__fn{};
-} // namespace __cpo
-} // namespace ranges
-
-#endif // _LIBCPP_STD_VER >= 20
-
-_LIBCPP_END_NAMESPACE_STD
-
-#endif // _LIBCPP___RANGES_DATA_H
diff --git a/libcxx/include/__ranges/owning_view.h b/libcxx/include/__ranges/owning_view.h
index 254bdb43291190..7e1b8318a2a4df 100644
--- a/libcxx/include/__ranges/owning_view.h
+++ b/libcxx/include/__ranges/owning_view.h
@@ -15,7 +15,6 @@
 #include <__config>
 #include <__ranges/access.h>
 #include <__ranges/concepts.h>
-#include <__ranges/data.h>
 #include <__ranges/empty.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/size.h>
diff --git a/libcxx/include/__ranges/rbegin.h b/libcxx/include/__ranges/rbegin.h
index 12e739e1a2b852..1e27ccc6648264 100644
--- a/libcxx/include/__ranges/rbegin.h
+++ b/libcxx/include/__ranges/rbegin.h
@@ -20,7 +20,6 @@
 #include <__type_traits/decay.h>
 #include <__type_traits/is_reference.h>
 #include <__type_traits/remove_cvref.h>
-#include <__type_traits/remove_reference.h>
 #include <__utility/auto_cast.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -85,34 +84,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/ref_view.h b/libcxx/include/__ranges/ref_view.h
index 5329d778dd30db..db858b8e79490d 100644
--- a/libcxx/include/__ranges/ref_view.h
+++ b/libcxx/include/__ranges/ref_view.h
@@ -19,7 +19,6 @@
 #include <__memory/addressof.h>
 #include <__ranges/access.h>
 #include <__ranges/concepts.h>
-#include <__ranges/data.h>
 #include <__ranges/empty.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/size.h>
diff --git a/libcxx/include/__ranges/rend.h b/libcxx/include/__ranges/rend.h
index 02b4c5999a7ebb..f0194bf192ca24 100644
--- a/libcxx/include/__ranges/rend.h
+++ b/libcxx/include/__ranges/rend.h
@@ -21,7 +21,6 @@
 #include <__type_traits/decay.h>
 #include <__type_traits/is_reference.h>
 #include <__type_traits/remove_cvref.h>
-#include <__type_traits/remove_reference.h>
 #include <__utility/auto_cast.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -89,33 +88,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/__ranges/view_interface.h b/libcxx/include/__ranges/view_interface.h
index 3bcfbaf3a2f9ed..f825941dcba4c4 100644
--- a/libcxx/include/__ranges/view_interface.h
+++ b/libcxx/include/__ranges/view_interface.h
@@ -20,6 +20,7 @@
 #include <__memory/pointer_traits.h>
 #include <__ranges/access.h>
 #include <__ranges/concepts.h>
+#include <__ranges/const_access.h>
 #include <__ranges/empty.h>
 #include <__ranges/size.h>
 #include <__type_traits/is_class.h>
@@ -72,6 +73,27 @@ class view_interface {
     }
   }
 
+  _LIBCPP_HIDE_FROM_ABI constexpr auto cbegin()
+    requires input_range<_Derived>
+  {
+    return ranges::cbegin(__derived());
+  }
+  _LIBCPP_HIDE_FROM_ABI constexpr auto cbegin() const
+    requires input_range<const _Derived>
+  {
+    return ranges::cbegin(__derived());
+  }
+  _LIBCPP_HIDE_FROM_ABI constexpr auto cend()
+    requires input_range<_Derived>
+  {
+    return ranges::cend(__derived());
+  }
+  _LIBCPP_HIDE_FROM_ABI constexpr auto cend() const
+    requires input_range<const _Derived>
+  {
+    return ranges::cend(__derived());
+  }
+
   template <class _D2 = _Derived>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit operator bool()
     requires requires(_D2& __t) { ranges::empty(__t); }
diff --git a/libcxx/include/iterator b/libcxx/include/iterator
index fca75f0a19ed1b..9a0c618f860cf8 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/module.modulemap b/libcxx/include/module.modulemap
index f193b5d95f49f5..f4b472131bfc41 100644
--- a/libcxx/include/module.modulemap
+++ b/libcxx/include/module.modulemap
@@ -1408,6 +1408,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" }
@@ -1690,9 +1691,11 @@ module std_private_ranges_all                        [system] {
   export std_private_functional_perfect_forward
   export std_private_ranges_owning_view
 }
+module std_private_ranges_as_const_view              [system] { header "__ranges/as_const_view.h" }
 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 "__ranges/const_access.h" }
 module std_private_ranges_concepts                   [system] {
   header "__ranges/concepts.h"
   export std_private_iterator_concepts
@@ -1703,7 +1706,6 @@ module std_private_ranges_counted                    [system] {
   export std_span
 }
 module std_private_ranges_dangling                   [system] { header "__ranges/dangling.h" }
-module std_private_ranges_data                       [system] { header "__ranges/data.h" }
 module std_private_ranges_drop_view                  [system] { header "__ranges/drop_view.h" }
 module std_private_ranges_drop_while_view            [system] { header "__ranges/drop_while_view.h" }
 module std_private_ranges_elements_view              [system] { header "__ranges/elements_view.h" }
diff --git a/libcxx/include/ranges b/libcxx/include/ranges
index fa35874265de67..45698ea2fe32c7 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -39,6 +39,10 @@ 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::cbegin(declval<R&>()));            // since C++23
+  template<range R>
+    using const_sentinel_t = decltype(ranges::cend(declval<R&>()));              // since C++23
   template<range R>
     using range_difference_t = iter_difference_t<iterator_t<R>>;
   template<sized_range R>
@@ -47,6 +51,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 = iter_const_reference_t<iterator_t<R>>;       // since C++23
   template<range R>
     using range_rvalue_reference_t = iter_rvalue_reference_t<iterator_t<R>>;
   template <range R>
@@ -93,6 +99,9 @@ namespace std::ranges {
   template<class T>
   concept viewable_range = see below;
 
+  template<class T>
+    concept constant_range = see below; // since C++23
+
   // [range.adaptor.object], range adaptor objects
   template<class D>
     requires is_class_v<D> && same_as<D, remove_cv_t<D>>
@@ -387,9 +396,9 @@ 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>
 #  include <__ranges/drop_view.h>
 #  include <__ranges/drop_while_view.h>
 #  include <__ranges/elements_view.h>
@@ -421,6 +430,7 @@ namespace std {
 #endif
 
 #if _LIBCPP_STD_VER >= 23
+#  include <__ranges/as_const_view.h>
 #  include <__ranges/as_rvalue_view.h>
 #  include <__ranges/chunk_by_view.h>
 #  include <__ranges/from_range.h>
diff --git a/libcxx/include/span b/libcxx/include/span
index da631cdc3f90e6..98a92822d630e4 100644
--- a/libcxx/include/span
+++ b/libcxx/include/span
@@ -65,7 +65,9 @@ public:
     using reference = element_type&;
     using const_reference = const element_type&;
     using iterator = implementation-defined;
+    using const_iterator = std::const_iterator<iterator>;                 // since C++23
     using reverse_iterator = std::reverse_iterator<iterator>;
+    using const_reverse_iterator = std::const_iterator<reverse_iterator>; // since C++23
     static constexpr size_type extent = Extent;
 
     // [span.cons], span constructors, copy, assignment, and destructor
@@ -115,8 +117,12 @@ public:
     // [span.iterators], span iterator support
     constexpr iterator begin() const noexcept;
     constexpr iterator end() const noexcept;
+    constexpr const_iterator cbegin() const noexcept;          // since C++23
+    constexpr const_iterator cend() const noexcept;            // since C++23
     constexpr reverse_iterator rbegin() const noexcept;
     constexpr reverse_iterator rend() const noexcept;
+    constexpr const_reverse_iterator crbegin() const noexcept; // since C++23
+    constexpr const_reverse_iterator crend() const noexcept;   // since C++23
 
 private:
     pointer data_;    // exposition only
@@ -152,12 +158,13 @@ template<class R>
 #include <__fwd/span.h>
 #include <__iterator/bounded_iter.h>
 #include <__iterator/concepts.h>
+#include <__iterator/const_iterator.h>
 #include <__iterator/iterator_traits.h>
 #include <__iterator/reverse_iterator.h>
 #include <__iterator/wrap_iter.h>
 #include <__memory/pointer_traits.h>
+#include <__ranges/access.h>
 #include <__ranges/concepts.h>
-#include <__ranges/data.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/enable_view.h>
 #include <__ranges/size.h>
@@ -167,6 +174,7 @@ template<class R>
 #include <__type_traits/is_convertible.h>
 #include <__type_traits/is_integral.h>
 #include <__type_traits/is_same.h>
+#include <__type_traits/is_volatile.h>
 #include <__type_traits/remove_const.h>
 #include <__type_traits/remove_cv.h>
 #include <__type_traits/remove_cvref.h>
@@ -242,6 +250,10 @@ public:
   using iterator = __wrap_iter<pointer>;
 #  endif
   using reverse_iterator = std::reverse_iterator<iterator>;
+#  if _LIBCPP_STD_VER >= 23
+  using const_iterator         = std::const_iterator<iterator>;
+  using const_reverse_iterator = std::const_iterator<reverse_iterator>;
+#  endif // _LIBCPP_STD_VER >= 23
 
   static constexpr size_type extent = _Extent;
 
@@ -390,8 +402,33 @@ public:
     return iterator(data() + size());
 #  endif
   }
+#  if _LIBCPP_STD_VER >= 23
+  _LIBCPP_HIDE_FROM_ABI constexpr const_iterator cbegin() const noexcept
+    requires(!is_volatile_v<_Tp>)
+  {
+    return begin();
+  }
+  _LIBCPP_HIDE_FROM_ABI constexpr const_iterator cend() const noexcept
+    requires(!is_volatile_v<_Tp>)
+  {
+    return end();
+  }
+#  endif // _LIBCPP_STD_VER >= 23
+
   _LIBCPP_HIDE_FROM_ABI constexpr reverse_iterator rbegin() const noexcept { return reverse_iterator(end()); }
   _LIBCPP_HIDE_FROM_ABI constexpr reverse_iterator rend() const noexcept { return reverse_iterator(begin()); }
+#  if _LIBCPP_STD_VER >= 23
+  _LIBCPP_HIDE_FROM_ABI constexpr const_reverse_iterator crbegin() const noexcept
+    requires(!is_volatile_v<_Tp>)
+  {
+    return rbegin();
+  }
+  _LIBCPP_HIDE_FROM_ABI constexpr const_reverse_iterator crend() const noexcept
+    requires(!is_volatile_v<_Tp>)
+  {
+    return rend();
+  }
+#  endif // _LIBCPP_STD_VER >= 23
 
   _LIBCPP_HIDE_FROM_ABI span<const byte, _Extent * sizeof(element_type)> __as_bytes() const noexcept {
     return span<const byte, _Extent * sizeof(element_type)>{reinterpret_cast<const byte*>(data()), size_bytes()};
@@ -423,6 +460,10 @@ public:
   using iterator = __wrap_iter<pointer>;
 #  endif
   using reverse_iterator = std::reverse_iterator<iterator>;
+#  if _LIBCPP_STD_VER >= 23
+  using const_iterator         = std::const_iterator<iterator>;
+  using const_reverse_iterator = std::const_iterator<reverse_iterator>;
+#  endif
 
   static constexpr size_type extent = dynamic_extent;
 
@@ -552,8 +593,33 @@ public:
     return iterator(data() + size());
 #  endif
   }
+#  if _LIBCPP_STD_VER >= 23
+  _LIBCPP_HIDE_FROM_ABI constexpr const_iterator cbegin() const noexcept
+    requires(!is_volatile_v<_Tp>)
+  {
+    return begin();
+  }
+  _LIBCPP_HIDE_FROM_ABI constexpr const_iterator cend() const noexcept
+    requires(!is_volatile_v<_Tp>)
+  {
+    return end();
+  }
+#  endif
+
   _LIBCPP_HIDE_FROM_ABI constexpr reverse_iterator rbegin() const noexcept { return reverse_iterator(end()); }
   _LIBCPP_HIDE_FROM_ABI constexpr reverse_iterator rend() const noexcept { return reverse_iterator(begin()); }
+#  if _LIBCPP_STD_VER >= 23
+  _LIBCPP_HIDE_FROM_ABI constexpr const_reverse_iterator crbegin() const noexcept
+    requires(!is_volatile_v<_Tp>)
+  {
+    return rbegin();
+  }
+  _LIBCPP_HIDE_FROM_ABI constexpr const_reverse_iterator crend() const noexcept
+    requires(!is_volatile_v<_Tp>)
+  {
+    return rend();
+  }
+#  endif
 
   _LIBCPP_HIDE_FROM_ABI span<const byte, dynamic_extent> __as_bytes() const noexcept {
     return {reinterpret_cast<const byte*>(data()), size_bytes()};
diff --git a/libcxx/include/string_view b/libcxx/include/string_view
index cf97e3a9be314d..63c319367c1394 100644
--- a/libcxx/include/string_view
+++ b/libcxx/include/string_view
@@ -219,8 +219,8 @@ namespace std {
 #include <__iterator/reverse_iterator.h>
 #include <__iterator/wrap_iter.h>
 #include <__memory/pointer_traits.h>
+#include <__ranges/access.h>
 #include <__ranges/concepts.h>
-#include <__ranges/data.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/enable_view.h>
 #include <__ranges/size.h>
diff --git a/libcxx/include/version b/libcxx/include/version
index dc1d3fd268ce83..4a3ef43b6f57d4 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -185,7 +185,7 @@ __cpp_lib_quoted_string_io                              201304L <iomanip>
 __cpp_lib_ranges                                        202406L <algorithm> <functional> <iterator>
                                                                 <memory> <ranges>
                                                         202110L // C++20
-__cpp_lib_ranges_as_const                               202207L <ranges>
+__cpp_lib_ranges_as_const                               202311L <ranges>
 __cpp_lib_ranges_as_rvalue                              202207L <ranges>
 __cpp_lib_ranges_chunk                                  202202L <ranges>
 __cpp_lib_ranges_chunk_by                               202202L <ranges>
@@ -483,7 +483,7 @@ __cpp_lib_void_t                                        201411L <type_traits>
 # define __cpp_lib_print                                202207L
 # undef  __cpp_lib_ranges
 # define __cpp_lib_ranges                               202406L
-// # define __cpp_lib_ranges_as_const                      202207L
+# define __cpp_lib_ranges_as_const                      202311L
 # define __cpp_lib_ranges_as_rvalue                     202207L
 // # define __cpp_lib_ranges_chunk                         202202L
 # define __cpp_lib_ranges_chunk_by                      202202L
diff --git a/libcxx/modules/std/iterator.inc b/libcxx/modules/std/iterator.inc
index 10c63d74e6e05e..5278ceaf2785b6 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/modules/std/ranges.inc b/libcxx/modules/std/ranges.inc
index f71efe948ede10..e111d818a3ae00 100644
--- a/libcxx/modules/std/ranges.inc
+++ b/libcxx/modules/std/ranges.inc
@@ -34,10 +34,14 @@ export namespace std {
 
     using std::ranges::borrowed_range;
 
-    // using std::ranges::const_iterator_t;
-    // using std::ranges::const_sentinel_t;
+#if _LIBCPP_STD_VER >= 23
+    using std::ranges::const_iterator_t;
+    using std::ranges::const_sentinel_t;
+#endif
     using std::ranges::iterator_t;
-    // using std::ranges::range_const_reference_t;
+#if _LIBCPP_STD_VER >= 23
+    using std::ranges::range_const_reference_t;
+#endif
     using std::ranges::range_common_reference_t;
     using std::ranges::range_difference_t;
     using std::ranges::range_reference_t;
@@ -58,7 +62,9 @@ export namespace std {
     // [range.refinements], other range refinements
     using std::ranges::bidirectional_range;
     using std::ranges::common_range;
-    // using std::ranges::constant_range;
+#if _LIBCPP_STD_VER >= 23
+    using std::ranges::constant_range;
+#endif
     using std::ranges::contiguous_range;
     using std::ranges::forward_range;
     using std::ranges::input_range;
diff --git a/libcxx/test/std/containers/views/views.span/range_concept_conformance.compile.pass.cpp b/libcxx/test/std/containers/views/views.span/range_concept_conformance.compile.pass.cpp
index fe28aeea3802b5..414d009ab93a06 100644
--- a/libcxx/test/std/containers/views/views.span/range_concept_conformance.compile.pass.cpp
+++ b/libcxx/test/std/containers/views/views.span/range_concept_conformance.compile.pass.cpp
@@ -15,6 +15,8 @@
 #include <concepts>
 #include <ranges>
 
+#include "test_macros.h"
+
 using range = std::span<int>;
 
 
@@ -35,3 +37,8 @@ static_assert(!std::ranges::view<range const> && !std::ranges::enable_view<range
 static_assert(std::ranges::sized_range<range const>);
 static_assert(std::ranges::borrowed_range<range const>);
 static_assert(std::ranges::viewable_range<range const>);
+
+#if TEST_STD_VER >= 23
+static_assert(std::same_as<std::ranges::const_iterator_t<range>, range::const_iterator>);
+static_assert(std::same_as<std::ranges::const_iterator_t<range const>, range::const_iterator>);
+#endif
diff --git a/libcxx/test/std/containers/views/views.span/span.iterators/begin.pass.cpp b/libcxx/test/std/containers/views/views.span/span.iterators/begin.pass.cpp
index f6b24017ffc1f9..70dda6e5c2a5e0 100644
--- a/libcxx/test/std/containers/views/views.span/span.iterators/begin.pass.cpp
+++ b/libcxx/test/std/containers/views/views.span/span.iterators/begin.pass.cpp
@@ -10,7 +10,7 @@
 // <span>
 
 // constexpr       iterator  begin() const noexcept;
-// constexpr const_iterator cbegin() const noexcept;
+// constexpr const_iterator cbegin() const noexcept; // since C++23
 
 #include <span>
 #include <cassert>
@@ -18,90 +18,68 @@
 
 #include "test_macros.h"
 
-template <class Span>
-constexpr bool testConstexprSpan(Span s)
-{
-    bool ret = true;
-    typename Span::iterator b = s.begin();
-
-    if (s.empty())
-    {
-        ret = ret && (b == s.end());
-    }
-    else
-    {
-        ret = ret && ( *b ==  s[0]);
-        ret = ret && (&*b == &s[0]);
-    }
-    return ret;
+template <class Span, class Iter>
+constexpr bool testSpanImpl(Span s, Iter first) {
+  bool ret = true;
+  if (s.empty()) {
+    ret = ret && (first == s.end());
+  } else {
+    ret = ret && (*first == s[0]);
+    ret = ret && (&*first == &s[0]);
+  }
+  return ret;
 }
 
+template <class EType, size_t Extent, class... Args>
+constexpr bool testSpan(Args&&... args) {
+  auto s1  = std::span<EType>(std::forward<Args>(args)...);
+  bool ret = true;
 
-template <class Span>
-void testRuntimeSpan(Span s)
-{
-    typename Span::iterator b = s.begin();
-
-    if (s.empty())
-    {
-        assert(b == s.end());
-    }
-    else
-    {
-        assert( *b ==  s[0]);
-        assert(&*b == &s[0]);
-    }
+  ret = ret && testSpanImpl(s1, s1.begin());
+#if TEST_STD_VER >= 23
+  ret = ret && testSpanImpl(s1, s1.cbegin());
+#endif
+
+  auto s2 = std::span<EType, Extent>(std::forward<Args>(args)...);
+  ret     = ret && testSpanImpl(s2, s2.begin());
+#if TEST_STD_VER >= 23
+  ret = ret && testSpanImpl(s2, s2.cbegin());
+#endif
+
+  return ret;
 }
 
-struct A{};
-bool operator==(A, A) {return true;}
-
-constexpr int iArr1[] = { 0,  1,  2,  3,  4,  5,  6,  7,  8,  9};
-          int iArr2[] = {10, 11, 12, 13, 14, 15, 16, 17, 18, 19};
-
-
-int main(int, char**)
-{
-    static_assert(testConstexprSpan(std::span<int>()),            "");
-    static_assert(testConstexprSpan(std::span<long>()),           "");
-    static_assert(testConstexprSpan(std::span<double>()),         "");
-    static_assert(testConstexprSpan(std::span<A>()),              "");
-    static_assert(testConstexprSpan(std::span<std::string>()),    "");
-
-    static_assert(testConstexprSpan(std::span<int, 0>()),         "");
-    static_assert(testConstexprSpan(std::span<long, 0>()),        "");
-    static_assert(testConstexprSpan(std::span<double, 0>()),      "");
-    static_assert(testConstexprSpan(std::span<A, 0>()),           "");
-    static_assert(testConstexprSpan(std::span<std::string, 0>()), "");
-
-    static_assert(testConstexprSpan(std::span<const int>(iArr1, 1)),    "");
-    static_assert(testConstexprSpan(std::span<const int>(iArr1, 2)),    "");
-    static_assert(testConstexprSpan(std::span<const int>(iArr1, 3)),    "");
-    static_assert(testConstexprSpan(std::span<const int>(iArr1, 4)),    "");
-    static_assert(testConstexprSpan(std::span<const int>(iArr1, 5)),    "");
-
-
-    testRuntimeSpan(std::span<int>        ());
-    testRuntimeSpan(std::span<long>       ());
-    testRuntimeSpan(std::span<double>     ());
-    testRuntimeSpan(std::span<A>          ());
-    testRuntimeSpan(std::span<std::string>());
-
-    testRuntimeSpan(std::span<int, 0>        ());
-    testRuntimeSpan(std::span<long, 0>       ());
-    testRuntimeSpan(std::span<double, 0>     ());
-    testRuntimeSpan(std::span<A, 0>          ());
-    testRuntimeSpan(std::span<std::string, 0>());
-
-    testRuntimeSpan(std::span<int>(iArr2, 1));
-    testRuntimeSpan(std::span<int>(iArr2, 2));
-    testRuntimeSpan(std::span<int>(iArr2, 3));
-    testRuntimeSpan(std::span<int>(iArr2, 4));
-    testRuntimeSpan(std::span<int>(iArr2, 5));
-
-    std::string s;
-    testRuntimeSpan(std::span<std::string>(&s, (std::size_t) 0));
-    testRuntimeSpan(std::span<std::string>(&s, 1));
+struct A {};
+bool operator==(A, A) { return true; }
+
+constexpr int iArr1[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+int iArr2[]           = {10, 11, 12, 13, 14, 15, 16, 17, 18, 19};
+
+int main(int, char**) {
+  ASSERT_RUNTIME_AND_CONSTEXPR(testSpan<int, 0>());
+  ASSERT_RUNTIME_AND_CONSTEXPR(testSpan<long, 0>());
+  ASSERT_RUNTIME_AND_CONSTEXPR(testSpan<double, 0>());
+  ASSERT_RUNTIME_AND_CONSTEXPR(testSpan<A, 0>());
+  ASSERT_RUNTIME_AND_CONSTEXPR(testSpan<std::string, 0>());
+
+  ASSERT_RUNTIME_AND_CONSTEXPR(testSpan<const int, 1>(iArr1, 1));
+  ASSERT_RUNTIME_AND_CONSTEXPR(testSpan<const int, 2>(iArr1, 2));
+  ASSERT_RUNTIME_AND_CONSTEXPR(testSpan<const int, 3>(iArr1, 3));
+  ASSERT_RUNTIME_AND_CONSTEXPR(testSpan<const int, 4>(iArr1, 4));
+  ASSERT_RUNTIME_AND_CONSTEXPR(testSpan<const int, 5>(iArr1, 5));
+
+  testSpan<int, 1>(iArr2, 1);
+  testSpan<int, 2>(iArr2, 2);
+  testSpan<int, 3>(iArr2, 3);
+  testSpan<int, 4>(iArr2, 4);
+  testSpan<int, 5>(iArr2, 5);
+
+  std::string s1;
+  constexpr static std::string s2;
+  testSpan<std::string, 0>(&s1, static_cast<size_t>(0));
+  ASSERT_RUNTIME_AND_CONSTEXPR(testSpan<const std::string, 0>(&s2, static_cast<size_t>(0)));
+  testSpan<std::string, 1>(&s1, 1);
+  ASSERT_RUNTIME_AND_CONSTEXPR(testSpan<const std::string, 1>(&s2, 1));
 
   return 0;
 }
diff --git a/libcxx/test/std/containers/views/views.span/span.iterators/end.pass.cpp b/libcxx/test/std/containers/views/views.span/span.iterators/end.pass.cpp
index d6aaf74d1058c3..5fc3cead94d6b2 100644
--- a/libcxx/test/std/containers/views/views.span/span.iterators/end.pass.cpp
+++ b/libcxx/test/std/containers/views/views.span/span.iterators/end.pass.cpp
@@ -10,7 +10,7 @@
 // <span>
 
 // constexpr       iterator  end() const noexcept;
-// constexpr const_iterator cend() const noexcept;
+// constexpr const_iterator cend() const noexcept; // since C++23
 
 #include <span>
 #include <cassert>
@@ -18,94 +18,69 @@
 
 #include "test_macros.h"
 
-template <class Span>
-constexpr bool testConstexprSpan(Span s)
-{
-    bool ret = true;
-    typename Span::iterator e = s.end();
-    if (s.empty())
-    {
-        ret = ret &&  (e == s.begin());
-    }
-    else
-    {
-        typename Span::const_pointer last = &*(s.begin() + s.size() - 1);
-        ret = ret &&  (e != s.begin());
-        ret = ret &&  (&*( e-1) == last);
-    }
-
-    ret = ret &&  (static_cast<std::size_t>(e - s.begin()) == s.size());
-    return ret;
+template <class Span, class Iter>
+constexpr bool testSpanImpl(Span s, Iter last) {
+  bool ret = true;
+  if (s.empty()) {
+    ret = ret && (last == s.begin());
+  } else {
+    ret = ret && (last != s.begin());
+    ret = ret && (&*(last - 1) == s.data() + s.size() - 1);
+  }
+  ret = ret && (static_cast<size_t>(last - s.begin()) == s.size());
+  return ret;
 }
 
-template <class Span>
-void testRuntimeSpan(Span s)
-{
-    typename Span::iterator e = s.end();
-    if (s.empty())
-    {
-        assert(e == s.begin());
-    }
-    else
-    {
-        typename Span::const_pointer last = &*(s.begin() + s.size() - 1);
-        assert(e != s.begin());
-        assert(&*( e-1) == last);
-    }
-
-    assert(static_cast<std::size_t>(e - s.begin()) == s.size());
-}
-
-
-struct A{};
-bool operator==(A, A) {return true;}
-
-constexpr int iArr1[] = { 0,  1,  2,  3,  4,  5,  6,  7,  8,  9};
-          int iArr2[] = {10, 11, 12, 13, 14, 15, 16, 17, 18, 19};
-
+template <class EType, size_t Extent, class... Args>
+constexpr bool testSpan(Args&&... args) {
+  auto s1  = std::span<EType>(std::forward<Args>(args)...);
+  bool ret = true;
 
-int main(int, char**)
-{
-    static_assert(testConstexprSpan(std::span<int>()),            "");
-    static_assert(testConstexprSpan(std::span<long>()),           "");
-    static_assert(testConstexprSpan(std::span<double>()),         "");
-    static_assert(testConstexprSpan(std::span<A>()),              "");
-    static_assert(testConstexprSpan(std::span<std::string>()),    "");
+  ret = ret && testSpanImpl(s1, s1.end());
+#if TEST_STD_VER >= 23
+  ret = ret && testSpanImpl(s1, s1.cend());
+#endif
 
-    static_assert(testConstexprSpan(std::span<int, 0>()),         "");
-    static_assert(testConstexprSpan(std::span<long, 0>()),        "");
-    static_assert(testConstexprSpan(std::span<double, 0>()),      "");
-    static_assert(testConstexprSpan(std::span<A, 0>()),           "");
-    static_assert(testConstexprSpan(std::span<std::string, 0>()), "");
+  auto s2 = std::span<EType, Extent>(std::forward<Args>(args)...);
+  ret     = ret && testSpanImpl(s2, s2.end());
+#if TEST_STD_VER >= 23
+  ret = ret && testSpanImpl(s2, s2.cend());
+#endif
 
-    static_assert(testConstexprSpan(std::span<const int>(iArr1, 1)),    "");
-    static_assert(testConstexprSpan(std::span<const int>(iArr1, 2)),    "");
-    static_assert(testConstexprSpan(std::span<const int>(iArr1, 3)),    "");
-    static_assert(testConstexprSpan(std::span<const int>(iArr1, 4)),    "");
-    static_assert(testConstexprSpan(std::span<const int>(iArr1, 5)),    "");
-
-
-    testRuntimeSpan(std::span<int>        ());
-    testRuntimeSpan(std::span<long>       ());
-    testRuntimeSpan(std::span<double>     ());
-    testRuntimeSpan(std::span<A>          ());
-    testRuntimeSpan(std::span<std::string>());
-
-    testRuntimeSpan(std::span<int, 0>        ());
-    testRuntimeSpan(std::span<long, 0>       ());
-    testRuntimeSpan(std::span<double, 0>     ());
-    testRuntimeSpan(std::span<A, 0>          ());
-    testRuntimeSpan(std::span<std::string, 0>());
-
-    testRuntimeSpan(std::span<int>(iArr2, 1));
-    testRuntimeSpan(std::span<int>(iArr2, 2));
-    testRuntimeSpan(std::span<int>(iArr2, 3));
-    testRuntimeSpan(std::span<int>(iArr2, 4));
-    testRuntimeSpan(std::span<int>(iArr2, 5));
+  return ret;
+}
 
-    std::string s;
-    testRuntimeSpan(std::span<std::string>(&s, (std::size_t) 0));
-    testRuntimeSpan(std::span<std::string>(&s, 1));
+struct A {};
+bool operator==(A, A) { return true; }
+
+constexpr int iArr1[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+int iArr2[]           = {10, 11, 12, 13, 14, 15, 16, 17, 18, 19};
+
+int main(int, char**) {
+  ASSERT_RUNTIME_AND_CONSTEXPR(testSpan<int, 0>());
+  ASSERT_RUNTIME_AND_CONSTEXPR(testSpan<long, 0>());
+  ASSERT_RUNTIME_AND_CONSTEXPR(testSpan<double, 0>());
+  ASSERT_RUNTIME_AND_CONSTEXPR(testSpan<A, 0>());
+  ASSERT_RUNTIME_AND_CONSTEXPR(testSpan<std::string, 0>());
+
+  ASSERT_RUNTIME_AND_CONSTEXPR(testSpan<const int, 1>(iArr1, 1));
+  ASSERT_RUNTIME_AND_CONSTEXPR(testSpan<const int, 2>(iArr1, 2));
+  ASSERT_RUNTIME_AND_CONSTEXPR(testSpan<const int, 3>(iArr1, 3));
+  ASSERT_RUNTIME_AND_CONSTEXPR(testSpan<const int, 4>(iArr1, 4));
+  ASSERT_RUNTIME_AND_CONSTEXPR(testSpan<const int, 5>(iArr1, 5));
+
+  testSpan<int, 1>(iArr2, 1);
+  testSpan<int, 2>(iArr2, 2);
+  testSpan<int, 3>(iArr2, 3);
+  testSpan<int, 4>(iArr2, 4);
+  testSpan<int, 5>(iArr2, 5);
+
+  std::string s1;
+  constexpr static std::string s2;
+  testSpan<std::string, 0>(&s1, static_cast<size_t>(0));
+  ASSERT_RUNTIME_AND_CONSTEXPR(testSpan<const std::string, 0>(&s2, static_cast<size_t>(0)));
+  testSpan<std::string, 1>(&s1, 1);
+  ASSERT_RUNTIME_AND_CONSTEXPR(testSpan<const std::string, 1>(&s2, 1));
 
   return 0;
 }
diff --git a/libcxx/test/std/containers/views/views.span/span.iterators/iterator.pass.cpp b/libcxx/test/std/containers/views/views.span/span.iterators/iterator.pass.cpp
index 13a7628e6043d9..7379ce61436d84 100644
--- a/libcxx/test/std/containers/views/views.span/span.iterators/iterator.pass.cpp
+++ b/libcxx/test/std/containers/views/views.span/span.iterators/iterator.pass.cpp
@@ -26,20 +26,17 @@ constexpr void test_type() {
   using C = std::span<T>;
   typename C::iterator ii1{}, ii2{};
   typename C::iterator ii4 = ii1;
-  // TODO Test against C++23 after implementing
-  //  P2278R4 cbegin should always return a constant iterator
-  // The means adjusting the #ifdef to guard against C++23.
-#ifdef __cpp_lib_ranges_as_const
+#if TEST_STD_VER >= 23
   typename C::const_iterator cii{};
 #endif
   assert(ii1 == ii2);
   assert(ii1 == ii4);
-#ifdef __cpp_lib_ranges_as_const
+#if TEST_STD_VER >= 23
   assert(ii1 == cii);
 #endif
 
   assert(!(ii1 != ii2));
-#ifdef __cpp_lib_ranges_as_const
+#if TEST_STD_VER >= 23
   assert(!(ii1 != cii));
 #endif
 
@@ -47,21 +44,21 @@ constexpr void test_type() {
   C c{&v, 1};
   assert(c.begin() == std::begin(c));
   assert(c.rbegin() == std::rbegin(c));
-#ifdef __cpp_lib_ranges_as_const
+#if TEST_STD_VER >= 23
   assert(c.cbegin() == std::cbegin(c));
   assert(c.crbegin() == std::crbegin(c));
 #endif
 
   assert(c.end() == std::end(c));
   assert(c.rend() == std::rend(c));
-#ifdef __cpp_lib_ranges_as_const
+#if TEST_STD_VER >= 23
   assert(c.cend() == std::cend(c));
   assert(c.crend() == std::crend(c));
 #endif
 
   assert(std::begin(c) != std::end(c));
   assert(std::rbegin(c) != std::rend(c));
-#ifdef __cpp_lib_ranges_as_const
+#if TEST_STD_VER >= 23
   assert(std::cbegin(c) != std::cend(c));
   assert(std::crbegin(c) != std::crend(c));
 #endif
@@ -70,7 +67,7 @@ constexpr void test_type() {
   std::same_as<std::strong_ordering> decltype(auto) r1 = ii1 <=> ii2;
   assert(r1 == std::strong_ordering::equal);
 
-#ifdef __cpp_lib_ranges_as_const
+#if TEST_STD_VER >= 23
   std::same_as<std::strong_ordering> decltype(auto) r2 = cii <=> ii2;
   assert(r2 == std::strong_ordering::equal);
 #endif
diff --git a/libcxx/test/std/containers/views/views.span/span.iterators/iterator_concept_conformance.compile.pass.cpp b/libcxx/test/std/containers/views/views.span/span.iterators/iterator_concept_conformance.compile.pass.cpp
index 1d9ea600bd2a06..3eedb24bee7b28 100644
--- a/libcxx/test/std/containers/views/views.span/span.iterators/iterator_concept_conformance.compile.pass.cpp
+++ b/libcxx/test/std/containers/views/views.span/span.iterators/iterator_concept_conformance.compile.pass.cpp
@@ -19,6 +19,11 @@ using iterator = std::span<int>::iterator;
 using reverse_iterator = std::span<int>::reverse_iterator;
 using value_type = int;
 
+#if TEST_STD_VER >= 23
+using const_iterator         = std::span<int>::const_iterator;
+using const_reverse_iterator = std::span<int>::const_reverse_iterator;
+#endif
+
 static_assert(std::contiguous_iterator<iterator>);
 LIBCPP_STATIC_ASSERT(std::__has_random_access_iterator_category<iterator>::value);
 static_assert(std::indirectly_writable<iterator, value_type>);
@@ -31,3 +36,18 @@ static_assert(std::indirectly_movable_storable<iterator, int*>);
 static_assert(std::indirectly_copyable<iterator, int*>);
 static_assert(std::indirectly_copyable_storable<iterator, int*>);
 static_assert(std::indirectly_swappable<iterator, iterator>);
+
+#if TEST_STD_VER >= 23
+static_assert(std::contiguous_iterator<const_iterator>);
+LIBCPP_STATIC_ASSERT(std::__has_random_access_iterator_category<const_iterator>::value);
+static_assert(std::sentinel_for<const_iterator, const_iterator>);
+static_assert(std::sentinel_for<const_iterator, iterator>);
+static_assert(std::sentinel_for<iterator, const_iterator>);
+static_assert(!std::sentinel_for<const_iterator, const_reverse_iterator>);
+static_assert(!std::sentinel_for<const_iterator, reverse_iterator>);
+static_assert(std::sized_sentinel_for<const_iterator, const_iterator>);
+static_assert(std::sized_sentinel_for<iterator, const_iterator>);
+static_assert(std::sized_sentinel_for<const_iterator, iterator>);
+static_assert(!std::sized_sentinel_for<const_iterator, const_reverse_iterator>);
+static_assert(!std::sized_sentinel_for<const_iterator, reverse_iterator>);
+#endif
diff --git a/libcxx/test/std/containers/views/views.span/span.iterators/rbegin.pass.cpp b/libcxx/test/std/containers/views/views.span/span.iterators/rbegin.pass.cpp
index 26e4389840857f..5dca0e89565470 100644
--- a/libcxx/test/std/containers/views/views.span/span.iterators/rbegin.pass.cpp
+++ b/libcxx/test/std/containers/views/views.span/span.iterators/rbegin.pass.cpp
@@ -9,8 +9,8 @@
 
 // <span>
 
-// constexpr       reverse_iterator  rbegin() const noexcept;
-// constexpr const_reverse_iterator crbegin() const noexcept;
+// constexpr       neverse_iterator  rbegin() const noexcept;
+// constexpr const_reverse_iterator crbegin() const noexcept; // since C++23
 
 #include <span>
 #include <cassert>
@@ -18,91 +18,70 @@
 
 #include "test_macros.h"
 
-template <class Span>
-constexpr bool testConstexprSpan(Span s)
-{
-    bool ret = true;
-    typename Span::reverse_iterator b = s.rbegin();
-    if (s.empty())
-    {
-        ret = ret &&  ( b ==  s.rend());
-    }
-    else
-    {
-        const typename Span::size_type last = s.size() - 1;
-        ret = ret && ( *b ==  s[last]);
-        ret = ret && (&*b == &s[last]);
-    }
-    return ret;
+template <class Span, class Iter>
+constexpr bool testSpanImpl(Span s, Iter rfirst) {
+  bool ret = true;
+  if (s.empty()) {
+    ret = ret && (rfirst == s.rend());
+  } else {
+    const typename Span::size_type last = s.size() - 1;
+
+    ret = ret && (*rfirst == s[last]);
+    ret = ret && (&*rfirst == &s[last]);
+  }
+  return ret;
 }
 
+template <class EType, size_t Extent, class... Args>
+constexpr bool testSpan(Args&&... args) {
+  auto s1  = std::span<EType>(std::forward<Args>(args)...);
+  bool ret = true;
 
-template <class Span>
-void testRuntimeSpan(Span s)
-{
-    typename Span::reverse_iterator b = s.rbegin();
-    if (s.empty())
-    {
-        assert(b == s.rend());
-    }
-    else
-    {
-        const typename Span::size_type last = s.size() - 1;
-        assert( *b ==  s[last]);
-        assert(&*b == &s[last]);
-    }
-}
-
-
-struct A{};
-bool operator==(A, A) {return true;}
-
-constexpr int iArr1[] = { 0,  1,  2,  3,  4,  5,  6,  7,  8,  9};
-          int iArr2[] = {10, 11, 12, 13, 14, 15, 16, 17, 18, 19};
-
-
-int main(int, char**)
-{
-    static_assert(testConstexprSpan(std::span<int>()),            "");
-    static_assert(testConstexprSpan(std::span<long>()),           "");
-    static_assert(testConstexprSpan(std::span<double>()),         "");
-    static_assert(testConstexprSpan(std::span<A>()),              "");
-    static_assert(testConstexprSpan(std::span<std::string>()),    "");
+  ret = ret && testSpanImpl(s1, s1.rbegin());
+#if TEST_STD_VER >= 23
+  ret = ret && testSpanImpl(s1, s1.crbegin());
+#endif
 
-    static_assert(testConstexprSpan(std::span<int, 0>()),         "");
-    static_assert(testConstexprSpan(std::span<long, 0>()),        "");
-    static_assert(testConstexprSpan(std::span<double, 0>()),      "");
-    static_assert(testConstexprSpan(std::span<A, 0>()),           "");
-    static_assert(testConstexprSpan(std::span<std::string, 0>()), "");
+  auto s2 = std::span<EType, Extent>(std::forward<Args>(args)...);
+  ret     = ret && testSpanImpl(s2, s2.rbegin());
+#if TEST_STD_VER >= 23
+  ret = ret && testSpanImpl(s2, s2.crbegin());
+#endif
 
-    static_assert(testConstexprSpan(std::span<const int>(iArr1, 1)),    "");
-    static_assert(testConstexprSpan(std::span<const int>(iArr1, 2)),    "");
-    static_assert(testConstexprSpan(std::span<const int>(iArr1, 3)),    "");
-    static_assert(testConstexprSpan(std::span<const int>(iArr1, 4)),    "");
-    static_assert(testConstexprSpan(std::span<const int>(iArr1, 5)),    "");
-
-
-    testRuntimeSpan(std::span<int>        ());
-    testRuntimeSpan(std::span<long>       ());
-    testRuntimeSpan(std::span<double>     ());
-    testRuntimeSpan(std::span<A>          ());
-    testRuntimeSpan(std::span<std::string>());
-
-    testRuntimeSpan(std::span<int, 0>        ());
-    testRuntimeSpan(std::span<long, 0>       ());
-    testRuntimeSpan(std::span<double, 0>     ());
-    testRuntimeSpan(std::span<A, 0>          ());
-    testRuntimeSpan(std::span<std::string, 0>());
-
-    testRuntimeSpan(std::span<int>(iArr2, 1));
-    testRuntimeSpan(std::span<int>(iArr2, 2));
-    testRuntimeSpan(std::span<int>(iArr2, 3));
-    testRuntimeSpan(std::span<int>(iArr2, 4));
-    testRuntimeSpan(std::span<int>(iArr2, 5));
+  return ret;
+}
 
-    std::string s;
-    testRuntimeSpan(std::span<std::string>(&s, static_cast<std::size_t>(0)));
-    testRuntimeSpan(std::span<std::string>(&s, 1));
+struct A {};
+bool operator==(A, A) { return true; }
+
+constexpr int iArr1[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+int iArr2[]           = {10, 11, 12, 13, 14, 15, 16, 17, 18, 19};
+
+int main(int, char**) {
+  ASSERT_RUNTIME_AND_CONSTEXPR(testSpan<int, 0>());
+  ASSERT_RUNTIME_AND_CONSTEXPR(testSpan<long, 0>());
+  ASSERT_RUNTIME_AND_CONSTEXPR(testSpan<double, 0>());
+  ASSERT_RUNTIME_AND_CONSTEXPR(testSpan<A, 0>());
+  ASSERT_RUNTIME_AND_CONSTEXPR(testSpan<std::string, 0>());
+
+  ASSERT_RUNTIME_AND_CONSTEXPR(testSpan<const int, 1>(iArr1, 1));
+  ASSERT_RUNTIME_AND_CONSTEXPR(testSpan<const int, 2>(iArr1, 2));
+  ASSERT_RUNTIME_AND_CONSTEXPR(testSpan<const int, 3>(iArr1, 3));
+  ASSERT_RUNTIME_AND_CONSTEXPR(testSpan<const int, 4>(iArr1, 4));
+  ASSERT_RUNTIME_AND_CONSTEXPR(testSpan<const int, 5>(iArr1, 5));
+
+  testSpan<int, 1>(iArr2, 1);
+  testSpan<int, 2>(iArr2, 2);
+  testSpan<int, 3>(iArr2, 3);
+  testSpan<int, 4>(iArr2, 4);
+  testSpan<int, 5>(iArr2, 5);
+
+  std::string s1;
+  constexpr static std::string s2;
+  testSpan<std::string, 0>(&s1, static_cast<size_t>(0));
+  ASSERT_RUNTIME_AND_CONSTEXPR(testSpan<const std::string, 0>(&s2, static_cast<size_t>(0)));
+  testSpan<std::string, 1>(&s1, 1);
+  ASSERT_RUNTIME_AND_CONSTEXPR(testSpan<const std::string, 1>(&s2, 1));
 
   return 0;
 }
diff --git a/libcxx/test/std/containers/views/views.span/span.iterators/rend.pass.cpp b/libcxx/test/std/containers/views/views.span/span.iterators/rend.pass.cpp
index ffec15c6b2492c..85e43c0a0bb424 100644
--- a/libcxx/test/std/containers/views/views.span/span.iterators/rend.pass.cpp
+++ b/libcxx/test/std/containers/views/views.span/span.iterators/rend.pass.cpp
@@ -10,7 +10,7 @@
 // <span>
 
 // constexpr       reverse_iterator  rend() const noexcept;
-// constexpr const_reverse_iterator crend() const noexcept;
+// constexpr const_reverse_iterator crend() const noexcept; // since C++23
 
 #include <span>
 #include <cassert>
@@ -18,90 +18,71 @@
 
 #include "test_macros.h"
 
-template <class Span>
-constexpr bool testConstexprSpan(Span s)
-{
-    bool ret = true;
-    typename Span::reverse_iterator e = s.rend();
-    if (s.empty())
-    {
-        ret = ret &&  (e == s.rbegin());
-    }
-    else
-    {
-        ret = ret &&  (e != s.rbegin());
-    }
-
-    ret = ret &&  (static_cast<std::size_t>(e - s.rbegin()) == s.size());
-    return ret;
-}
-
-template <class Span>
-void testRuntimeSpan(Span s)
-{
-    typename Span::reverse_iterator e = s.rend();
-    if (s.empty())
-    {
-        assert(e == s.rbegin());
-    }
-    else
-    {
-        assert(e != s.rbegin());
-    }
-
-    assert(static_cast<std::size_t>(e - s.rbegin()) == s.size());
-}
-
-
-struct A{};
-bool operator==(A, A) {return true;}
+template <class Span, class Iter>
+constexpr bool testSpanImpl(Span s, Iter rlast) {
+  bool ret = true;
+  if (s.empty()) {
+    ret = ret && (rlast == s.rbegin());
+  } else {
+    ret = ret && (rlast != s.rbegin());
+    ret = ret && (rlast == std::make_reverse_iterator(s.begin()));
+  }
 
-constexpr int iArr1[] = { 0,  1,  2,  3,  4,  5,  6,  7,  8,  9};
-          int iArr2[] = {10, 11, 12, 13, 14, 15, 16, 17, 18, 19};
+  ret = ret && (static_cast<std::size_t>(rlast - s.rbegin()) == s.size());
 
+  return ret;
+}
 
-int main(int, char**)
-{
-    static_assert(testConstexprSpan(std::span<int>()),            "");
-    static_assert(testConstexprSpan(std::span<long>()),           "");
-    static_assert(testConstexprSpan(std::span<double>()),         "");
-    static_assert(testConstexprSpan(std::span<A>()),              "");
-    static_assert(testConstexprSpan(std::span<std::string>()),    "");
-
-    static_assert(testConstexprSpan(std::span<int, 0>()),         "");
-    static_assert(testConstexprSpan(std::span<long, 0>()),        "");
-    static_assert(testConstexprSpan(std::span<double, 0>()),      "");
-    static_assert(testConstexprSpan(std::span<A, 0>()),           "");
-    static_assert(testConstexprSpan(std::span<std::string, 0>()), "");
-
-    static_assert(testConstexprSpan(std::span<const int>(iArr1, 1)),    "");
-    static_assert(testConstexprSpan(std::span<const int>(iArr1, 2)),    "");
-    static_assert(testConstexprSpan(std::span<const int>(iArr1, 3)),    "");
-    static_assert(testConstexprSpan(std::span<const int>(iArr1, 4)),    "");
-    static_assert(testConstexprSpan(std::span<const int>(iArr1, 5)),    "");
-
+template <class EType, size_t Extent, class... Args>
+constexpr bool testSpan(Args&&... args) {
+  auto s1  = std::span<EType>(std::forward<Args>(args)...);
+  bool ret = true;
 
-    testRuntimeSpan(std::span<int>        ());
-    testRuntimeSpan(std::span<long>       ());
-    testRuntimeSpan(std::span<double>     ());
-    testRuntimeSpan(std::span<A>          ());
-    testRuntimeSpan(std::span<std::string>());
+  ret = ret && testSpanImpl(s1, s1.rend());
+#if TEST_STD_VER >= 23
+  ret = ret && testSpanImpl(s1, s1.crend());
+#endif
 
-    testRuntimeSpan(std::span<int, 0>        ());
-    testRuntimeSpan(std::span<long, 0>       ());
-    testRuntimeSpan(std::span<double, 0>     ());
-    testRuntimeSpan(std::span<A, 0>          ());
-    testRuntimeSpan(std::span<std::string, 0>());
+  auto s2 = std::span<EType, Extent>(std::forward<Args>(args)...);
+  ret     = ret && testSpanImpl(s2, s2.rend());
+#if TEST_STD_VER >= 23
+  ret = ret && testSpanImpl(s2, s2.crend());
+#endif
 
-    testRuntimeSpan(std::span<int>(iArr2, 1));
-    testRuntimeSpan(std::span<int>(iArr2, 2));
-    testRuntimeSpan(std::span<int>(iArr2, 3));
-    testRuntimeSpan(std::span<int>(iArr2, 4));
-    testRuntimeSpan(std::span<int>(iArr2, 5));
+  return ret;
+}
 
-    std::string s;
-    testRuntimeSpan(std::span<std::string>(&s, (std::size_t) 0));
-    testRuntimeSpan(std::span<std::string>(&s, 1));
+struct A {};
+bool operator==(A, A) { return true; }
+
+constexpr int iArr1[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+int iArr2[]           = {10, 11, 12, 13, 14, 15, 16, 17, 18, 19};
+
+int main(int, char**) {
+  ASSERT_RUNTIME_AND_CONSTEXPR(testSpan<int, 0>());
+  ASSERT_RUNTIME_AND_CONSTEXPR(testSpan<long, 0>());
+  ASSERT_RUNTIME_AND_CONSTEXPR(testSpan<double, 0>());
+  ASSERT_RUNTIME_AND_CONSTEXPR(testSpan<A, 0>());
+  ASSERT_RUNTIME_AND_CONSTEXPR(testSpan<std::string, 0>());
+
+  ASSERT_RUNTIME_AND_CONSTEXPR(testSpan<const int, 1>(iArr1, 1));
+  ASSERT_RUNTIME_AND_CONSTEXPR(testSpan<const int, 2>(iArr1, 2));
+  ASSERT_RUNTIME_AND_CONSTEXPR(testSpan<const int, 3>(iArr1, 3));
+  ASSERT_RUNTIME_AND_CONSTEXPR(testSpan<const int, 4>(iArr1, 4));
+  ASSERT_RUNTIME_AND_CONSTEXPR(testSpan<const int, 5>(iArr1, 5));
+
+  testSpan<int, 1>(iArr2, 1);
+  testSpan<int, 2>(iArr2, 2);
+  testSpan<int, 3>(iArr2, 3);
+  testSpan<int, 4>(iArr2, 4);
+  testSpan<int, 5>(iArr2, 5);
+
+  std::string s1;
+  constexpr static std::string s2;
+  testSpan<std::string, 0>(&s1, static_cast<size_t>(0));
+  ASSERT_RUNTIME_AND_CONSTEXPR(testSpan<const std::string, 0>(&s2, static_cast<size_t>(0)));
+  testSpan<std::string, 1>(&s1, 1);
+  ASSERT_RUNTIME_AND_CONSTEXPR(testSpan<const std::string, 1>(&s2, 1));
 
   return 0;
 }
diff --git a/libcxx/test/std/iterators/const.iterators/alias.pass.cpp b/libcxx/test/std/iterators/const.iterators/alias.pass.cpp
new file mode 100644
index 00000000000000..10c0665764cc86
--- /dev/null
+++ b/libcxx/test/std/iterators/const.iterators/alias.pass.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// std::const_iterator
+
+#include <iterator>
+#include <list>
+#include <ranges>
+#include "test_macros.h"
+
+ASSERT_SAME_TYPE(std::const_iterator<int*>, std::basic_const_iterator<int*>);
+ASSERT_SAME_TYPE(std::const_iterator<const int*>, const int*);
+ASSERT_SAME_TYPE(std::const_sentinel<int*>, std::basic_const_iterator<int*>);
+ASSERT_SAME_TYPE(std::const_sentinel<const int*>, const int*);
+ASSERT_SAME_TYPE(std::const_sentinel<std::default_sentinel_t>, std::default_sentinel_t);
+
+using list_iterator       = std::list<int>::iterator;
+using list_const_iterator = std::list<int>::const_iterator;
+
+ASSERT_SAME_TYPE(std::const_iterator<list_iterator>, std::basic_const_iterator<list_iterator>);
+ASSERT_SAME_TYPE(std::const_iterator<list_const_iterator>, list_const_iterator);
+ASSERT_SAME_TYPE(std::const_sentinel<list_iterator>, std::basic_const_iterator<list_iterator>);
+ASSERT_SAME_TYPE(std::const_sentinel<list_const_iterator>, list_const_iterator);
+
+int main() { return 0; }
diff --git a/libcxx/test/std/iterators/const.iterators/iterator.pass.cpp b/libcxx/test/std/iterators/const.iterators/iterator.pass.cpp
new file mode 100644
index 00000000000000..9163976deb8f12
--- /dev/null
+++ b/libcxx/test/std/iterators/const.iterators/iterator.pass.cpp
@@ -0,0 +1,139 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// std::basic_const_iterator
+
+#include <iterator>
+#include <list>
+#include <ranges>
+#include <vector>
+#include "test_macros.h"
+#include "test_iterators.h"
+#include "type_algorithms.h"
+
+template <class T>
+concept has_iterator_category = requires { typename T::iterator_category; };
+
+template <class It>
+constexpr bool check_category_and_concept() {
+  using ConstIt = std::basic_const_iterator<It>;
+
+  if constexpr (std::contiguous_iterator<It>) {
+    ASSERT_SAME_TYPE(typename ConstIt::iterator_concept, std::contiguous_iterator_tag);
+    static_assert(std::contiguous_iterator<ConstIt>);
+  } else if constexpr (std::random_access_iterator<It>) {
+    ASSERT_SAME_TYPE(typename ConstIt::iterator_concept, std::random_access_iterator_tag);
+    static_assert(!std::contiguous_iterator<ConstIt>);
+    static_assert(std::random_access_iterator<ConstIt>);
+  } else if constexpr (std::bidirectional_iterator<It>) {
+    ASSERT_SAME_TYPE(typename ConstIt::iterator_concept, std::bidirectional_iterator_tag);
+    static_assert(!std::random_access_iterator<ConstIt>);
+    static_assert(std::bidirectional_iterator<ConstIt>);
+  } else if constexpr (std::forward_iterator<It>) {
+    ASSERT_SAME_TYPE(typename ConstIt::iterator_concept, std::forward_iterator_tag);
+    static_assert(!std::bidirectional_iterator<ConstIt>);
+    static_assert(std::forward_iterator<ConstIt>);
+  } else {
+    ASSERT_SAME_TYPE(typename ConstIt::iterator_concept, std::input_iterator_tag);
+    static_assert(!std::forward_iterator<ConstIt>);
+    static_assert(std::input_iterator<ConstIt>);
+  }
+
+  if constexpr (std::forward_iterator<It>) {
+    ASSERT_SAME_TYPE(typename ConstIt::iterator_category, typename std::iterator_traits<It>::iterator_category);
+  } else {
+    static_assert(!has_iterator_category<ConstIt>);
+  }
+
+  return true;
+}
+
+constexpr bool test_p2836r1() {
+  auto f = [](std::vector<int>::const_iterator) {};
+
+  auto v = std::vector<int>();
+  {
+    auto i1 = std::ranges::cbegin(v);
+    f(i1);
+  }
+
+  auto t = v | std::views::take_while([](int const x) { return x < 100; });
+  {
+    auto i2 = std::ranges::cbegin(t);
+    f(i2);
+  }
+
+  return true;
+}
+
+constexpr bool test_basic_operations() {
+  struct S {
+    int x;
+  };
+  S arr[10]                           = {};
+  std::basic_const_iterator<S*> first = arr;
+  std::basic_const_iterator<S*> last  = arr + 10;
+
+  for (auto it = first; it != last; ++it) {
+    (void)*it;
+    (void)it->x;
+    (void)iter_move(it);
+  }
+  static_assert(!std::is_invocable_v<decltype(std::ranges::iter_swap), decltype(first), decltype(first)>);
+
+  assert(++first == arr + 1);
+  assert(--first == arr + 0);
+  assert(first++ == arr + 0);
+  assert(first-- == arr + 1);
+
+  assert(first + 3 == arr + 3);
+  assert(last - 1 == arr + 9);
+
+  first += 3;
+  assert(first == arr + 3);
+  first -= 2;
+  assert(first == arr + 1);
+  --first;
+
+  assert(first < last);
+  assert(last > first);
+  assert(first <= last);
+  assert(last >= first);
+
+  assert(first < arr + 1);
+  assert(arr + 1 > first);
+  assert(first <= arr + 1);
+  assert(arr + 1 >= first);
+
+  assert((first <=> last) < 0);
+  assert((first <=> arr + 1) < 0);
+  assert((arr + 1 <=> first) > 0);
+
+  return true;
+}
+
+int main() {
+  types::for_each(types::cpp20_input_iterator_list<int*>{}, []<class It>() {
+    using ConstIt = std::basic_const_iterator<It>;
+    ASSERT_SAME_TYPE(typename ConstIt::value_type, int);
+    static_assert(check_category_and_concept<It>());
+
+    ASSERT_SAME_TYPE(std::iter_reference_t<ConstIt>, const int&);
+    ASSERT_SAME_TYPE(std::iter_rvalue_reference_t<ConstIt>, const int&&);
+  });
+
+  test_p2836r1();
+  static_assert(test_p2836r1());
+
+  test_basic_operations();
+  static_assert(test_basic_operations());
+
+  return 0;
+}
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp
index 8493505ef0236d..df8f1c96960d2f 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp
@@ -19,7 +19,7 @@
     __cpp_lib_default_template_type_for_algorithm_values    202403L [C++26]
     __cpp_lib_ranges                                        202110L [C++20]
                                                             202406L [C++23]
-    __cpp_lib_ranges_as_const                               202207L [C++23]
+    __cpp_lib_ranges_as_const                               202311L [C++23]
     __cpp_lib_ranges_as_rvalue                              202207L [C++23]
     __cpp_lib_ranges_chunk                                  202202L [C++23]
     __cpp_lib_ranges_chunk_by                               202202L [C++23]
@@ -250,17 +250,11 @@
 #   error "__cpp_lib_ranges should have the value 202406L in c++23"
 # endif
 
-# if !defined(_LIBCPP_VERSION)
-#   ifndef __cpp_lib_ranges_as_const
-#     error "__cpp_lib_ranges_as_const should be defined in c++23"
-#   endif
-#   if __cpp_lib_ranges_as_const != 202207L
-#     error "__cpp_lib_ranges_as_const should have the value 202207L in c++23"
-#   endif
-# else // _LIBCPP_VERSION
-#   ifdef __cpp_lib_ranges_as_const
-#     error "__cpp_lib_ranges_as_const should not be defined because it is unimplemented in libc++!"
-#   endif
+# ifndef __cpp_lib_ranges_as_const
+#   error "__cpp_lib_ranges_as_const should be defined in c++23"
+# endif
+# if __cpp_lib_ranges_as_const != 202311L
+#   error "__cpp_lib_ranges_as_const should have the value 202311L in c++23"
 # endif
 
 # ifndef __cpp_lib_ranges_as_rvalue
@@ -369,17 +363,11 @@
 #   error "__cpp_lib_ranges should have the value 202406L in c++26"
 # endif
 
-# if !defined(_LIBCPP_VERSION)
-#   ifndef __cpp_lib_ranges_as_const
-#     error "__cpp_lib_ranges_as_const should be defined in c++26"
-#   endif
-#   if __cpp_lib_ranges_as_const != 202207L
-#     error "__cpp_lib_ranges_as_const should have the value 202207L in c++26"
-#   endif
-# else // _LIBCPP_VERSION
-#   ifdef __cpp_lib_ranges_as_const
-#     error "__cpp_lib_ranges_as_const should not be defined because it is unimplemented in libc++!"
-#   endif
+# ifndef __cpp_lib_ranges_as_const
+#   error "__cpp_lib_ranges_as_const should be defined in c++26"
+# endif
+# if __cpp_lib_ranges_as_const != 202311L
+#   error "__cpp_lib_ranges_as_const should have the value 202311L in c++26"
 # endif
 
 # ifndef __cpp_lib_ranges_as_rvalue
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
index a022c90e166c8d..af7ce69b345d91 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
@@ -168,7 +168,7 @@
     __cpp_lib_quoted_string_io                              201304L [C++14]
     __cpp_lib_ranges                                        202110L [C++20]
                                                             202406L [C++23]
-    __cpp_lib_ranges_as_const                               202207L [C++23]
+    __cpp_lib_ranges_as_const                               202311L [C++23]
     __cpp_lib_ranges_as_rvalue                              202207L [C++23]
     __cpp_lib_ranges_chunk                                  202202L [C++23]
     __cpp_lib_ranges_chunk_by                               202202L [C++23]
@@ -5624,17 +5624,11 @@
 #   error "__cpp_lib_ranges should have the value 202406L in c++23"
 # endif
 
-# if !defined(_LIBCPP_VERSION)
-#   ifndef __cpp_lib_ranges_as_const
-#     error "__cpp_lib_ranges_as_const should be defined in c++23"
-#   endif
-#   if __cpp_lib_ranges_as_const != 202207L
-#     error "__cpp_lib_ranges_as_const should have the value 202207L in c++23"
-#   endif
-# else // _LIBCPP_VERSION
-#   ifdef __cpp_lib_ranges_as_const
-#     error "__cpp_lib_ranges_as_const should not be defined because it is unimplemented in libc++!"
-#   endif
+# ifndef __cpp_lib_ranges_as_const
+#   error "__cpp_lib_ranges_as_const should be defined in c++23"
+# endif
+# if __cpp_lib_ranges_as_const != 202311L
+#   error "__cpp_lib_ranges_as_const should have the value 202311L in c++23"
 # endif
 
 # ifndef __cpp_lib_ranges_as_rvalue
@@ -7475,17 +7469,11 @@
 #   error "__cpp_lib_ranges should have the value 202406L in c++26"
 # endif
 
-# if !defined(_LIBCPP_VERSION)
-#   ifndef __cpp_lib_ranges_as_const
-#     error "__cpp_lib_ranges_as_const should be defined in c++26"
-#   endif
-#   if __cpp_lib_ranges_as_const != 202207L
-#     error "__cpp_lib_ranges_as_const should have the value 202207L in c++26"
-#   endif
-# else // _LIBCPP_VERSION
-#   ifdef __cpp_lib_ranges_as_const
-#     error "__cpp_lib_ranges_as_const should not be defined because it is unimplemented in libc++!"
-#   endif
+# ifndef __cpp_lib_ranges_as_const
+#   error "__cpp_lib_ranges_as_const should be defined in c++26"
+# endif
+# if __cpp_lib_ranges_as_const != 202311L
+#   error "__cpp_lib_ranges_as_const should have the value 202311L in c++26"
 # endif
 
 # ifndef __cpp_lib_ranges_as_rvalue
diff --git a/libcxx/test/std/ranges/range.access/begin.pass.cpp b/libcxx/test/std/ranges/range.access/begin.pass.cpp
index 5ca3d59abb1407..7f2357b319a8a0 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>
 
@@ -19,7 +19,9 @@
 #include "test_iterators.h"
 
 using RangeBeginT = decltype(std::ranges::begin);
+#if TEST_STD_VER < 23
 using RangeCBeginT = decltype(std::ranges::cbegin);
+#endif // TEST_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 TEST_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 // TEST_STD_VER < 23
 
 struct Incomplete;
 static_assert(!std::is_invocable_v<RangeBeginT, Incomplete(&&)[]>);
 static_assert(!std::is_invocable_v<RangeBeginT, const Incomplete(&&)[]>);
+#if TEST_STD_VER < 23
 static_assert(!std::is_invocable_v<RangeCBeginT, Incomplete(&&)[]>);
 static_assert(!std::is_invocable_v<RangeCBeginT, const Incomplete(&&)[]>);
+#endif // TEST_STD_VER < 23
 
 static_assert(!std::is_invocable_v<RangeBeginT, Incomplete(&&)[10]>);
 static_assert(!std::is_invocable_v<RangeBeginT, const Incomplete(&&)[10]>);
+#if TEST_STD_VER < 23
 static_assert(!std::is_invocable_v<RangeCBeginT, Incomplete(&&)[10]>);
 static_assert(!std::is_invocable_v<RangeCBeginT, const Incomplete(&&)[10]>);
+#endif // TEST_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 TEST_STD_VER < 23
 LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeCBeginT, Incomplete(&)[]>);
 LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeCBeginT, const Incomplete(&)[]>);
+#endif // TEST_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 TEST_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 // TEST_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 TEST_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 // TEST_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 TEST_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 // TEST_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 TEST_STD_VER < 23
+  assert(std::ranges::cbegin(a) == a);
+  assert(std::ranges::cbegin(b) == b);
   assert(std::ranges::cbegin(c) == c);
+#endif // TEST_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 TEST_STD_VER < 23
 static_assert(!std::is_invocable_v<RangeCBeginT, NonConstBeginMember &>);
 static_assert(!std::is_invocable_v<RangeCBeginT, NonConstBeginMember const&>);
+#endif // TEST_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 TEST_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 // TEST_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 TEST_STD_VER < 23
 static_assert( std::is_invocable_v<RangeCBeginT, BeginFunction const&>);
 static_assert( std::is_invocable_v<RangeCBeginT, BeginFunction &>);
+#endif // TEST_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 TEST_STD_VER < 23
 static_assert(!std::is_invocable_v<RangeCBeginT, BeginFunctionByValue>);
+#endif // TEST_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 TEST_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 // TEST_STD_VER < 23
 
   return true;
 }
 
 
 ASSERT_NOEXCEPT(std::ranges::begin(std::declval<int (&)[10]>()));
+#if TEST_STD_VER < 23
 ASSERT_NOEXCEPT(std::ranges::cbegin(std::declval<int (&)[10]>()));
+#endif // TEST_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 TEST_STD_VER < 23
 static_assert(noexcept(std::ranges::cbegin(ntmb)));
+#endif // TEST_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 TEST_STD_VER < 23
 static_assert(noexcept(std::ranges::cbegin(ntab)));
+#endif // TEST_STD_VER < 23
 
 struct NoThrowMemberBeginReturnsRef {
   ThrowingIterator<int>& begin() const noexcept; // auto(t.begin()) may throw
 } ntmbrr;
 static_assert(!noexcept(std::ranges::begin(ntmbrr)));
+#if TEST_STD_VER < 23
 static_assert(!noexcept(std::ranges::cbegin(ntmbrr)));
+#endif // TEST_STD_VER < 23
 
 struct BeginReturnsArrayRef {
     auto begin() const noexcept -> int(&)[10];
 } brar;
 static_assert(noexcept(std::ranges::begin(brar)));
+#if TEST_STD_VER < 23
 static_assert(noexcept(std::ranges::cbegin(brar)));
+#endif // TEST_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 TEST_STD_VER < 23
 static_assert(!std::is_invocable_v<RangeCBeginT, Holder<Incomplete>*>);
 static_assert(!std::is_invocable_v<RangeCBeginT, Holder<Incomplete>*&>);
+#endif // TEST_STD_VER < 23
 
 int main(int, char**) {
   static_assert(testReturnTypes());
diff --git a/libcxx/test/std/ranges/range.access/cbegin.pass.cpp b/libcxx/test/std/ranges/range.access/cbegin.pass.cpp
new file mode 100644
index 00000000000000..a2da7cdf3b6d39
--- /dev/null
+++ b/libcxx/test/std/ranges/range.access/cbegin.pass.cpp
@@ -0,0 +1,207 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// std::ranges::cbegin
+// std::ranges::crbegin
+
+#include <ranges>
+
+#include <cassert>
+#include <utility>
+#include "almost_satisfies_types.h"
+#include "test_macros.h"
+#include "test_iterators.h"
+
+using RangeCBeginT  = decltype(std::ranges::cbegin);
+using RangeCRBeginT = decltype(std::ranges::crbegin);
+
+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 (&)[]>);
+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 (&)[]>);
+
+static_assert(!std::is_invocable_v<RangeCBeginT, InputRangeNotDerivedFrom>);
+static_assert(!std::is_invocable_v<RangeCBeginT, InputRangeNotIndirectlyReadable>);
+static_assert(!std::is_invocable_v<RangeCBeginT, InputRangeNotInputOrOutputIterator>);
+static_assert(!std::is_invocable_v<RangeCBeginT, InputRangeNotSentinelSemiregular>);
+static_assert(!std::is_invocable_v<RangeCBeginT, InputRangeNotSentinelEqualityComparableWith>);
+
+static_assert(!std::is_invocable_v<RangeCRBeginT, BidirectionalRangeNotDerivedFrom>);
+static_assert(!std::is_invocable_v<RangeCRBeginT, BidirectionalRangeNotSentinelSemiregular>);
+static_assert(!std::is_invocable_v<RangeCRBeginT, BidirectionalRangeNotSentinelWeaklyEqualityComparableWith>);
+static_assert(!std::is_invocable_v<RangeCRBeginT, BidirectionalRangeNotDecrementable>);
+
+struct Incomplete;
+
+static_assert(!std::is_invocable_v<RangeCBeginT, Incomplete (&&)[]>);
+static_assert(!std::is_invocable_v<RangeCBeginT, const Incomplete (&&)[]>);
+static_assert(!std::is_invocable_v<RangeCBeginT, Incomplete (&&)[10]>);
+static_assert(!std::is_invocable_v<RangeCBeginT, const Incomplete (&&)[10]>);
+
+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]>);
+
+// This case is IFNDR; we handle it SFINAE-friendly.
+LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeCBeginT, Incomplete (&)[]>);
+LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeCBeginT, const Incomplete (&)[]>);
+LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeCRBeginT, Incomplete (&)[]>);
+LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeCRBeginT, const Incomplete (&)[]>);
+
+// This case is IFNDR; we handle it SFINAE-friendly.
+LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeCBeginT, Incomplete (&)[10]>);
+LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeCBeginT, const Incomplete (&)[10]>);
+LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeCRBeginT, Incomplete (&)[10]>);
+LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeCRBeginT, const Incomplete (&)[10]>);
+
+struct NonborrowingRange {
+  int x;
+  constexpr const int* begin() const { return &x; }
+  constexpr const int* rbegin() const { return &x; }
+  constexpr const int* end() const { return &x; }
+  constexpr const int* rend() const { return &x; }
+};
+
+// Ensure that we can't call with rvalues with borrowing disabled.
+static_assert(std::is_invocable_v<RangeCBeginT, NonborrowingRange&>);
+static_assert(!std::is_invocable_v<RangeCBeginT, NonborrowingRange&&>);
+static_assert(std::is_invocable_v<RangeCBeginT, NonborrowingRange const&>);
+static_assert(!std::is_invocable_v<RangeCBeginT, NonborrowingRange const&&>);
+static_assert(std::is_invocable_v<RangeCRBeginT, NonborrowingRange&>);
+static_assert(!std::is_invocable_v<RangeCRBeginT, NonborrowingRange&&>);
+static_assert(std::is_invocable_v<RangeCRBeginT, NonborrowingRange const&>);
+static_assert(!std::is_invocable_v<RangeCRBeginT, NonborrowingRange const&&>);
+
+constexpr bool testReturnTypes() {
+  int* a[2];
+  int b[2][2];
+  struct PossiblyConstRange {
+    char*& begin();
+    char*& end();
+    const short*& begin() const;
+    const short*& end() const;
+    int*& rbegin();
+    int*& rend();
+    const long*& rbegin() const;
+    const long*& rend() const;
+  } c;
+  struct AlwaysConstRange {
+    const char*& begin();
+    const char*& end();
+    const short*& begin() const;
+    const short*& end() const;
+    const int*& rbegin();
+    const int*& rend();
+    const long*& rbegin() const;
+    const long*& rend() const;
+  } d;
+  struct NeverConstRange {
+    char*& begin();
+    char*& end();
+    short*& begin() const;
+    short& end() const;
+    int*& rbegin();
+    int*& rend();
+    long*& rbegin() const;
+    long*& rend() const;
+  } e;
+
+  static_assert(!std::ranges::constant_range<PossiblyConstRange>);
+  static_assert(std::ranges::constant_range<const PossiblyConstRange>);
+  static_assert(std::ranges::constant_range<AlwaysConstRange>);
+  static_assert(std::ranges::constant_range<const AlwaysConstRange>);
+  static_assert(!std::ranges::constant_range<NeverConstRange>);
+  static_assert(!std::ranges::constant_range<const NeverConstRange>);
+
+  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)), const short*);
+  ASSERT_SAME_TYPE(decltype(std::ranges::cbegin(d)), const char*);
+  ASSERT_SAME_TYPE(decltype(std::ranges::cbegin(e)), std::basic_const_iterator<char*>);
+
+  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)), const long*);
+  ASSERT_SAME_TYPE(decltype(std::ranges::crbegin(d)), const int*);
+  ASSERT_SAME_TYPE(decltype(std::ranges::crbegin(e)), std::basic_const_iterator<int*>);
+
+  return true;
+}
+
+constexpr bool testArray() {
+  int a[2];
+  int b[2][2];
+  NonborrowingRange c[2];
+
+  assert(std::ranges::cbegin(a) == a);
+  assert(std::ranges::cbegin(b) == b);
+  assert(std::ranges::cbegin(c) == c);
+
+  assert(std::ranges::crbegin(a).base() == a + 2);
+  assert(std::ranges::crbegin(b).base() == b + 2);
+  assert(std::ranges::crbegin(c).base() == c + 2);
+
+  return true;
+}
+
+struct BorrowingRange {
+  int* begin() const;
+  int* end() const;
+};
+template <>
+inline constexpr bool std::ranges::enable_borrowed_range<BorrowingRange> = true;
+
+static_assert(std::is_invocable_v<RangeCBeginT, BorrowingRange>);
+static_assert(std::is_invocable_v<RangeCBeginT, const BorrowingRange>);
+static_assert(std::is_invocable_v<RangeCBeginT, BorrowingRange&>);
+static_assert(std::is_invocable_v<RangeCBeginT, const BorrowingRange&>);
+static_assert(std::is_invocable_v<RangeCRBeginT, BorrowingRange>);
+static_assert(std::is_invocable_v<RangeCRBeginT, const BorrowingRange>);
+static_assert(std::is_invocable_v<RangeCRBeginT, BorrowingRange&>);
+static_assert(std::is_invocable_v<RangeCRBeginT, const BorrowingRange&>);
+
+struct NoThrowBeginThrowingEnd {
+  const int* begin() const noexcept;
+  const int* end() const;
+} ntbte;
+static_assert(noexcept(std::ranges::cbegin(ntbte)));
+static_assert(!noexcept(std::ranges::crbegin(ntbte)));
+
+struct ThrowingBeginNoThrowEnd {
+  const int* begin() const;
+  const int* end() const noexcept;
+} tbnte;
+static_assert(!noexcept(std::ranges::cbegin(tbnte)));
+static_assert(noexcept(std::ranges::crbegin(tbnte)));
+
+// Test ADL-proofing.
+struct Incomplete;
+template <class T>
+struct Holder {
+  T t;
+};
+static_assert(!std::is_invocable_v<RangeCBeginT, Holder<Incomplete>*>);
+static_assert(!std::is_invocable_v<RangeCBeginT, Holder<Incomplete>*&>);
+static_assert(!std::is_invocable_v<RangeCRBeginT, Holder<Incomplete>*>);
+static_assert(!std::is_invocable_v<RangeCRBeginT, Holder<Incomplete>*&>);
+
+int main(int, char**) {
+  static_assert(testReturnTypes());
+
+  testArray();
+  static_assert(testArray());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.access/cbegin.verify.cpp b/libcxx/test/std/ranges/range.access/cbegin.verify.cpp
new file mode 100644
index 00000000000000..18ac8ae65e3383
--- /dev/null
+++ b/libcxx/test/std/ranges/range.access/cbegin.verify.cpp
@@ -0,0 +1,25 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// std::ranges::cbegin
+
+#include <ranges>
+
+struct NonBorrowedRange {
+  int* begin() const;
+  int* end() const;
+};
+static_assert(!std::ranges::enable_borrowed_range<NonBorrowedRange>);
+
+// Verify that if the expression is an rvalue and `enable_borrowed_range` is false, `ranges::cbegin` is ill-formed.
+void test() {
+  std::ranges::cbegin(NonBorrowedRange());
+  // expected-error-re at -1 {{{{no matching function for call to object of type 'const (std::ranges::)?__cbegin::__fn'}}}}
+}
diff --git a/libcxx/test/std/ranges/range.access/cend.pass.cpp b/libcxx/test/std/ranges/range.access/cend.pass.cpp
new file mode 100644
index 00000000000000..86cb8ac160eb37
--- /dev/null
+++ b/libcxx/test/std/ranges/range.access/cend.pass.cpp
@@ -0,0 +1,207 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// std::ranges::cend
+// std::ranges::crend
+
+#include <ranges>
+
+#include <cassert>
+#include <utility>
+#include "almost_satisfies_types.h"
+#include "test_macros.h"
+#include "test_iterators.h"
+
+using RangeCEndT  = decltype(std::ranges::cend);
+using RangeCREndT = decltype(std::ranges::crend);
+
+static_assert(!std::is_invocable_v<RangeCEndT, int (&&)[10]>);
+static_assert(std::is_invocable_v<RangeCEndT, int (&)[10]>);
+static_assert(!std::is_invocable_v<RangeCEndT, int (&&)[]>);
+static_assert(!std::is_invocable_v<RangeCEndT, int (&)[]>);
+static_assert(!std::is_invocable_v<RangeCREndT, int (&&)[10]>);
+static_assert(std::is_invocable_v<RangeCREndT, int (&)[10]>);
+static_assert(!std::is_invocable_v<RangeCREndT, int (&&)[]>);
+static_assert(!std::is_invocable_v<RangeCREndT, int (&)[]>);
+
+static_assert(!std::is_invocable_v<RangeCEndT, InputRangeNotDerivedFrom>);
+static_assert(!std::is_invocable_v<RangeCEndT, InputRangeNotIndirectlyReadable>);
+static_assert(!std::is_invocable_v<RangeCEndT, InputRangeNotInputOrOutputIterator>);
+static_assert(!std::is_invocable_v<RangeCEndT, InputRangeNotSentinelSemiregular>);
+static_assert(!std::is_invocable_v<RangeCEndT, InputRangeNotSentinelEqualityComparableWith>);
+
+static_assert(!std::is_invocable_v<RangeCREndT, BidirectionalRangeNotDerivedFrom>);
+static_assert(!std::is_invocable_v<RangeCREndT, BidirectionalRangeNotSentinelSemiregular>);
+static_assert(!std::is_invocable_v<RangeCREndT, BidirectionalRangeNotSentinelWeaklyEqualityComparableWith>);
+static_assert(!std::is_invocable_v<RangeCREndT, BidirectionalRangeNotDecrementable>);
+
+struct Incomplete;
+
+static_assert(!std::is_invocable_v<RangeCEndT, Incomplete (&&)[]>);
+static_assert(!std::is_invocable_v<RangeCEndT, const Incomplete (&&)[]>);
+static_assert(!std::is_invocable_v<RangeCEndT, Incomplete (&&)[10]>);
+static_assert(!std::is_invocable_v<RangeCEndT, const Incomplete (&&)[10]>);
+
+static_assert(!std::is_invocable_v<RangeCREndT, Incomplete (&&)[]>);
+static_assert(!std::is_invocable_v<RangeCREndT, const Incomplete (&&)[]>);
+static_assert(!std::is_invocable_v<RangeCREndT, Incomplete (&&)[10]>);
+static_assert(!std::is_invocable_v<RangeCREndT, const Incomplete (&&)[10]>);
+
+// This case is IFNDR; we handle it SFINAE-friendly.
+LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeCEndT, Incomplete (&)[]>);
+LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeCEndT, const Incomplete (&)[]>);
+LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeCREndT, Incomplete (&)[]>);
+LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeCREndT, const Incomplete (&)[]>);
+
+// This case is IFNDR; we handle it SFINAE-friendly.
+LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeCEndT, Incomplete (&)[10]>);
+LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeCEndT, const Incomplete (&)[10]>);
+LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeCREndT, Incomplete (&)[10]>);
+LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeCREndT, const Incomplete (&)[10]>);
+
+struct NonborrowingRange {
+  int x;
+  constexpr const int* begin() const { return &x; }
+  constexpr const int* rbegin() const { return &x; }
+  constexpr const int* end() const { return &x; }
+  constexpr const int* rend() const { return &x; }
+};
+
+// Ensure that we can't call with rvalues with borrowing disabled.
+static_assert(std::is_invocable_v<RangeCEndT, NonborrowingRange&>);
+static_assert(!std::is_invocable_v<RangeCEndT, NonborrowingRange&&>);
+static_assert(std::is_invocable_v<RangeCEndT, NonborrowingRange const&>);
+static_assert(!std::is_invocable_v<RangeCEndT, NonborrowingRange const&&>);
+static_assert(std::is_invocable_v<RangeCREndT, NonborrowingRange&>);
+static_assert(!std::is_invocable_v<RangeCREndT, NonborrowingRange&&>);
+static_assert(std::is_invocable_v<RangeCREndT, NonborrowingRange const&>);
+static_assert(!std::is_invocable_v<RangeCREndT, NonborrowingRange const&&>);
+
+constexpr bool testReturnTypes() {
+  int* a[2];
+  int b[2][2];
+  struct PossiblyConstRange {
+    char*& begin();
+    char*& end();
+    const short*& begin() const;
+    const short*& end() const;
+    int*& rbegin();
+    int*& rend();
+    const long*& rbegin() const;
+    const long*& rend() const;
+  } c;
+  struct AlwaysConstRange {
+    const char*& begin();
+    const char*& end();
+    const short*& begin() const;
+    const short*& end() const;
+    const int*& rbegin();
+    const int*& rend();
+    const long*& rbegin() const;
+    const long*& rend() const;
+  } d;
+  struct NeverConstRange {
+    char*& begin();
+    char*& end();
+    short*& begin() const;
+    short& end() const;
+    int*& rbegin();
+    int*& rend();
+    long*& rbegin() const;
+    long*& rend() const;
+  } e;
+
+  static_assert(!std::ranges::constant_range<PossiblyConstRange>);
+  static_assert(std::ranges::constant_range<const PossiblyConstRange>);
+  static_assert(std::ranges::constant_range<AlwaysConstRange>);
+  static_assert(std::ranges::constant_range<const AlwaysConstRange>);
+  static_assert(!std::ranges::constant_range<NeverConstRange>);
+  static_assert(!std::ranges::constant_range<const NeverConstRange>);
+
+  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)), const short*);
+  ASSERT_SAME_TYPE(decltype(std::ranges::cend(d)), const char*);
+  ASSERT_SAME_TYPE(decltype(std::ranges::cend(e)), std::basic_const_iterator<char*>);
+
+  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)), const long*);
+  ASSERT_SAME_TYPE(decltype(std::ranges::crend(d)), const int*);
+  ASSERT_SAME_TYPE(decltype(std::ranges::crend(e)), std::basic_const_iterator<int*>);
+
+  return true;
+}
+
+constexpr bool testArray() {
+  int a[2];
+  int b[2][2];
+  NonborrowingRange c[2];
+
+  assert(std::ranges::cend(a) == a + 2);
+  assert(std::ranges::cend(b) == b + 2);
+  assert(std::ranges::cend(c) == c + 2);
+
+  assert(std::ranges::crend(a).base() == a);
+  assert(std::ranges::crend(b).base() == b);
+  assert(std::ranges::crend(c).base() == c);
+
+  return true;
+}
+
+struct BorrowingRange {
+  int* begin() const;
+  int* end() const;
+};
+template <>
+inline constexpr bool std::ranges::enable_borrowed_range<BorrowingRange> = true;
+
+static_assert(std::is_invocable_v<RangeCEndT, BorrowingRange>);
+static_assert(std::is_invocable_v<RangeCEndT, const BorrowingRange>);
+static_assert(std::is_invocable_v<RangeCEndT, BorrowingRange&>);
+static_assert(std::is_invocable_v<RangeCEndT, const BorrowingRange&>);
+static_assert(std::is_invocable_v<RangeCREndT, BorrowingRange>);
+static_assert(std::is_invocable_v<RangeCREndT, const BorrowingRange>);
+static_assert(std::is_invocable_v<RangeCREndT, BorrowingRange&>);
+static_assert(std::is_invocable_v<RangeCREndT, const BorrowingRange&>);
+
+struct NoThrowEndThrowingEnd {
+  const int* begin() const noexcept;
+  const int* end() const;
+} ntbte;
+static_assert(!noexcept(std::ranges::cend(ntbte)));
+static_assert(noexcept(std::ranges::crend(ntbte)));
+
+struct ThrowingEndNoThrowEnd {
+  const int* begin() const;
+  const int* end() const noexcept;
+} tbnte;
+static_assert(noexcept(std::ranges::cend(tbnte)));
+static_assert(!noexcept(std::ranges::crend(tbnte)));
+
+// Test ADL-proofing.
+struct Incomplete;
+template <class T>
+struct Holder {
+  T t;
+};
+static_assert(!std::is_invocable_v<RangeCEndT, Holder<Incomplete>*>);
+static_assert(!std::is_invocable_v<RangeCEndT, Holder<Incomplete>*&>);
+static_assert(!std::is_invocable_v<RangeCREndT, Holder<Incomplete>*>);
+static_assert(!std::is_invocable_v<RangeCREndT, Holder<Incomplete>*&>);
+
+int main(int, char**) {
+  static_assert(testReturnTypes());
+
+  testArray();
+  static_assert(testArray());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.access/cend.verify.cpp b/libcxx/test/std/ranges/range.access/cend.verify.cpp
new file mode 100644
index 00000000000000..e50b90b4fbd490
--- /dev/null
+++ b/libcxx/test/std/ranges/range.access/cend.verify.cpp
@@ -0,0 +1,25 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// std::ranges::cend
+
+#include <ranges>
+
+struct NonBorrowedRange {
+  int* begin() const;
+  int* end() const;
+};
+static_assert(!std::ranges::enable_borrowed_range<NonBorrowedRange>);
+
+// Verify that if the expression is an rvalue and `enable_borrowed_range` is false, `ranges::cend` is ill-formed.
+void test() {
+  std::ranges::cend(NonBorrowedRange());
+  // expected-error-re at -1 {{{{no matching function for call to object of type 'const (std::ranges::)?__cend::__fn'}}}}
+}
diff --git a/libcxx/test/std/ranges/range.access/data.pass.cpp b/libcxx/test/std/ranges/range.access/data.pass.cpp
index 357e52a1462751..03bbaf0b1ca0f5 100644
--- a/libcxx/test/std/ranges/range.access/data.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/data.pass.cpp
@@ -9,6 +9,7 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 
 // std::ranges::data
+// std::ranges::cdata
 
 #include <ranges>
 
@@ -20,6 +21,14 @@
 using RangeDataT = decltype(std::ranges::data);
 using RangeCDataT = decltype(std::ranges::cdata);
 
+#if TEST_STD_VER < 23
+#  define STD_VER_20(...) __VA_ARGS__
+#  define STD_VER_23(...) static_assert(true)
+#else
+#  define STD_VER_20(...) static_assert(true)
+#  define STD_VER_23(...) __VA_ARGS__
+#endif
+
 static int globalBuff[2];
 
 struct Incomplete;
@@ -38,9 +47,26 @@ static_assert(!std::is_invocable_v<RangeCDataT, int [1]>);
 static_assert(!std::is_invocable_v<RangeCDataT, int (&&)[1]>);
 static_assert( std::is_invocable_v<RangeCDataT, int (&)[1]>);
 
+struct DataMemberNonRange {
+  int x;
+  constexpr const int* data() const { return &x; }
+};
+static_assert(std::is_invocable_v<RangeDataT, DataMemberNonRange&>);
+static_assert(!std::is_invocable_v<RangeDataT, DataMemberNonRange&&>);
+static_assert(std::is_invocable_v<RangeDataT, DataMemberNonRange const&>);
+static_assert(!std::is_invocable_v<RangeDataT, DataMemberNonRange const&&>);
+#if TEST_STD_VER < 23
+static_assert(std::is_invocable_v<RangeCDataT, DataMemberNonRange&>);
+static_assert(!std::is_invocable_v<RangeCDataT, DataMemberNonRange&&>);
+static_assert(std::is_invocable_v<RangeCDataT, DataMemberNonRange const&>);
+static_assert(!std::is_invocable_v<RangeCDataT, DataMemberNonRange const&&>);
+#endif
+
 struct DataMember {
   int x;
   constexpr const int *data() const { return &x; }
+  const int* begin() const;
+  const int* end() const;
 };
 static_assert( std::is_invocable_v<RangeDataT, DataMember &>);
 static_assert(!std::is_invocable_v<RangeDataT, DataMember &&>);
@@ -66,16 +92,51 @@ constexpr bool testReturnTypes() {
     struct D {
       char*& data();
       short*& data() const;
+      int* begin();
+      int* end();
     };
     ASSERT_SAME_TYPE(decltype(std::ranges::data(std::declval<D&>())), char*);
     static_assert(!std::is_invocable_v<RangeDataT, D&&>);
     ASSERT_SAME_TYPE(decltype(std::ranges::data(std::declval<const D&>())), short*);
     static_assert(!std::is_invocable_v<RangeDataT, const D&&>);
-    ASSERT_SAME_TYPE(decltype(std::ranges::cdata(std::declval<D&>())), short*);
+
+    static_assert(!std::is_invocable_v<RangeCDataT, D&&>);
+    static_assert(!std::is_invocable_v<RangeCDataT, const D&&>);
+    STD_VER_20(ASSERT_SAME_TYPE(decltype(std::ranges::cdata(std::declval<D&>())), short*));
+    STD_VER_23(ASSERT_SAME_TYPE(decltype(std::ranges::cdata(std::declval<D&>())), const char*));
+    STD_VER_20(ASSERT_SAME_TYPE(decltype(std::ranges::cdata(std::declval<const D&>())), short*));
+    STD_VER_23(static_assert(!std::is_invocable_v<RangeCDataT, const D&>));
+  }
+#if TEST_STD_VER >= 23
+  {
+    struct D {
+      char*& data();
+      short*& data() const;
+      int* begin();
+      int* end();
+      int* begin() const;
+      int* end() const;
+    };
+    ASSERT_SAME_TYPE(decltype(std::ranges::cdata(std::declval<D&>())), const char*);
+    static_assert(!std::is_invocable_v<RangeCDataT, D&&>);
+    ASSERT_SAME_TYPE(decltype(std::ranges::cdata(std::declval<const D&>())), const short*);
+    static_assert(!std::is_invocable_v<RangeCDataT, const D&&>);
+  }
+  {
+    struct D {
+      char*& data();
+      short*& data() const;
+      int* begin();
+      int* end();
+      const int* begin() const;
+      const int* end() const;
+    };
+    ASSERT_SAME_TYPE(decltype(std::ranges::cdata(std::declval<D&>())), const short*);
     static_assert(!std::is_invocable_v<RangeCDataT, D&&>);
-    ASSERT_SAME_TYPE(decltype(std::ranges::cdata(std::declval<const D&>())), short*);
+    ASSERT_SAME_TYPE(decltype(std::ranges::cdata(std::declval<const D&>())), const short*);
     static_assert(!std::is_invocable_v<RangeCDataT, const D&&>);
   }
+#endif // TEST_STD_VER >= 23
   {
     struct NC {
       char *begin() const;
@@ -88,10 +149,16 @@ constexpr bool testReturnTypes() {
     static_assert(!std::is_invocable_v<RangeDataT, NC&&>);
     ASSERT_SAME_TYPE(decltype(std::ranges::data(std::declval<const NC&>())), char*);
     static_assert(!std::is_invocable_v<RangeDataT, const NC&&>);
-    ASSERT_SAME_TYPE(decltype(std::ranges::cdata(std::declval<NC&>())), char*);
+
     static_assert(!std::is_invocable_v<RangeCDataT, NC&&>);
-    ASSERT_SAME_TYPE(decltype(std::ranges::cdata(std::declval<const NC&>())), char*);
     static_assert(!std::is_invocable_v<RangeCDataT, const NC&&>);
+#if TEST_STD_VER < 23
+    ASSERT_SAME_TYPE(decltype(std::ranges::cdata(std::declval<NC&>())), char*);
+    ASSERT_SAME_TYPE(decltype(std::ranges::cdata(std::declval<const NC&>())), char*);
+#else
+    ASSERT_SAME_TYPE(decltype(std::ranges::cdata(std::declval<NC&>())), const int*);
+    ASSERT_SAME_TYPE(decltype(std::ranges::cdata(std::declval<const NC&>())), const char*);
+#endif
   }
   return true;
 }
@@ -121,10 +188,14 @@ static_assert(!std::is_invocable_v<RangeCDataT, PtrConvertibleDataMember const&>
 struct NonConstDataMember {
   int x;
   constexpr int *data() { return &x; }
+  int* begin();
+  int* end();
 };
 
 struct EnabledBorrowingDataMember {
   constexpr int *data() { return &globalBuff[0]; }
+  int* begin();
+  int* end();
 };
 template<>
 inline constexpr bool std::ranges::enable_borrowed_range<EnabledBorrowingDataMember> = true;
@@ -133,6 +204,7 @@ struct DataMemberAndBegin {
   int x;
   constexpr const int *data() const { return &x; }
   const int *begin() const;
+  const int* end() const;
 };
 
 constexpr bool testDataMember() {
@@ -142,15 +214,13 @@ constexpr bool testDataMember() {
 
   NonConstDataMember b;
   assert(std::ranges::data(b) == &b.x);
-  static_assert(!std::is_invocable_v<RangeCDataT, decltype((b))>);
+  STD_VER_20(static_assert(!std::is_invocable_v<RangeCDataT, decltype((b))>));
+  STD_VER_23(assert(std::ranges::cdata(b) == &b.x));
 
   EnabledBorrowingDataMember c;
   assert(std::ranges::data(std::move(c)) == &globalBuff[0]);
-  static_assert(!std::is_invocable_v<RangeCDataT, decltype(std::move(c))>);
-
-  DataMemberAndBegin d;
-  assert(std::ranges::data(d) == &d.x);
-  assert(std::ranges::cdata(d) == &d.x);
+  STD_VER_20(static_assert(!std::is_invocable_v<RangeCDataT, decltype((c))>));
+  STD_VER_23(assert(std::ranges::data(c) == &globalBuff[0]));
 
   return true;
 }
@@ -161,6 +231,7 @@ struct BeginMemberContiguousIterator {
   int buff[8];
 
   constexpr ContiguousIter begin() const { return ContiguousIter(buff); }
+  constexpr ContiguousIter end() const { return ContiguousIter(buff); }
 };
 static_assert( std::is_invocable_v<RangeDataT, BeginMemberContiguousIterator &>);
 static_assert(!std::is_invocable_v<RangeDataT, BeginMemberContiguousIterator &&>);
@@ -175,6 +246,7 @@ struct BeginMemberRandomAccess {
   int buff[8];
 
   random_access_iterator<const int*> begin() const;
+  random_access_iterator<const int*> end() const;
 };
 static_assert(!std::is_invocable_v<RangeDataT, BeginMemberRandomAccess&>);
 static_assert(!std::is_invocable_v<RangeDataT, BeginMemberRandomAccess&&>);
@@ -191,6 +263,7 @@ struct BeginFriendContiguousIterator {
   friend constexpr ContiguousIter begin(const BeginFriendContiguousIterator &iter) {
     return ContiguousIter(iter.buff);
   }
+  friend constexpr ContiguousIter end(const BeginFriendContiguousIterator& iter) { return ContiguousIter(iter.buff); }
 };
 static_assert( std::is_invocable_v<RangeDataT, BeginMemberContiguousIterator &>);
 static_assert(!std::is_invocable_v<RangeDataT, BeginMemberContiguousIterator &&>);
@@ -202,7 +275,8 @@ static_assert( std::is_invocable_v<RangeCDataT, BeginMemberContiguousIterator co
 static_assert(!std::is_invocable_v<RangeCDataT, BeginMemberContiguousIterator const&&>);
 
 struct BeginFriendRandomAccess {
-  friend random_access_iterator<const int*> begin(const BeginFriendRandomAccess iter);
+  friend random_access_iterator<const int*> begin(const BeginFriendRandomAccess& iter);
+  friend random_access_iterator<const int*> end(const BeginFriendRandomAccess& iter);
 };
 static_assert(!std::is_invocable_v<RangeDataT, BeginFriendRandomAccess&>);
 static_assert(!std::is_invocable_v<RangeDataT, BeginFriendRandomAccess&&>);
@@ -217,6 +291,7 @@ struct BeginMemberRvalue {
   int buff[8];
 
   ContiguousIter begin() &&;
+  ContiguousIter end() &&;
 };
 static_assert(!std::is_invocable_v<RangeDataT, BeginMemberRvalue&>);
 static_assert(!std::is_invocable_v<RangeDataT, BeginMemberRvalue&&>);
@@ -229,6 +304,7 @@ static_assert(!std::is_invocable_v<RangeCDataT, BeginMemberRvalue const&&>);
 
 struct BeginMemberBorrowingEnabled {
   constexpr contiguous_iterator<int*> begin() { return contiguous_iterator<int*>{&globalBuff[1]}; }
+  constexpr contiguous_iterator<int*> end() { return contiguous_iterator<int*>{&globalBuff[2]}; }
 };
 template<>
 inline constexpr bool std::ranges::enable_borrowed_range<BeginMemberBorrowingEnabled> = true;
@@ -236,11 +312,28 @@ static_assert( std::is_invocable_v<RangeDataT, BeginMemberBorrowingEnabled &>);
 static_assert( std::is_invocable_v<RangeDataT, BeginMemberBorrowingEnabled &&>);
 static_assert(!std::is_invocable_v<RangeDataT, BeginMemberBorrowingEnabled const&>);
 static_assert(!std::is_invocable_v<RangeDataT, BeginMemberBorrowingEnabled const&&>);
-static_assert(!std::is_invocable_v<RangeCDataT, BeginMemberBorrowingEnabled &>);
-static_assert(!std::is_invocable_v<RangeCDataT, BeginMemberBorrowingEnabled &&>);
+STD_VER_20(static_assert(!std::is_invocable_v<RangeCDataT, BeginMemberBorrowingEnabled&>));
+STD_VER_23(static_assert(std::is_invocable_v<RangeCDataT, BeginMemberBorrowingEnabled&>));
+STD_VER_20(static_assert(!std::is_invocable_v<RangeCDataT, BeginMemberBorrowingEnabled&&>));
+STD_VER_23(static_assert(std::is_invocable_v<RangeCDataT, BeginMemberBorrowingEnabled&&>));
 static_assert(!std::is_invocable_v<RangeCDataT, BeginMemberBorrowingEnabled const&>);
 static_assert(!std::is_invocable_v<RangeCDataT, BeginMemberBorrowingEnabled const&&>);
 
+struct ConstBeginMemberBorrowingEnabled {
+  constexpr contiguous_iterator<int*> begin() const { return contiguous_iterator<int*>{&globalBuff[1]}; }
+  constexpr contiguous_iterator<int*> end() const { return contiguous_iterator<int*>{&globalBuff[2]}; }
+};
+template <>
+inline constexpr bool std::ranges::enable_borrowed_range<ConstBeginMemberBorrowingEnabled> = true;
+static_assert(std::is_invocable_v<RangeDataT, ConstBeginMemberBorrowingEnabled&>);
+static_assert(std::is_invocable_v<RangeDataT, ConstBeginMemberBorrowingEnabled&&>);
+static_assert(std::is_invocable_v<RangeDataT, ConstBeginMemberBorrowingEnabled const&>);
+static_assert(std::is_invocable_v<RangeDataT, ConstBeginMemberBorrowingEnabled const&&>);
+static_assert(std::is_invocable_v<RangeCDataT, ConstBeginMemberBorrowingEnabled&>);
+static_assert(std::is_invocable_v<RangeCDataT, ConstBeginMemberBorrowingEnabled&&>);
+static_assert(std::is_invocable_v<RangeCDataT, ConstBeginMemberBorrowingEnabled const&>);
+static_assert(std::is_invocable_v<RangeCDataT, ConstBeginMemberBorrowingEnabled const&&>);
+
 constexpr bool testViaRangesBegin() {
   int arr[2];
   assert(std::ranges::data(arr) == arr + 0);
@@ -256,7 +349,12 @@ constexpr bool testViaRangesBegin() {
 
   BeginMemberBorrowingEnabled c;
   assert(std::ranges::data(std::move(c)) == &globalBuff[1]);
-  static_assert(!std::is_invocable_v<RangeCDataT, decltype(std::move(c))>);
+  STD_VER_20(static_assert(!std::is_invocable_v<RangeCDataT, decltype(std::move(c))>));
+  STD_VER_23(static_assert(std::is_invocable_v<RangeCDataT, decltype(std::move(c))>));
+
+  ConstBeginMemberBorrowingEnabled d;
+  assert(std::ranges::data(std::move(d)) == &globalBuff[1]);
+  assert(std::ranges::cdata(std::move(d)) == &globalBuff[1]);
 
   return true;
 }
diff --git a/libcxx/test/std/ranges/range.access/end.pass.cpp b/libcxx/test/std/ranges/range.access/end.pass.cpp
index 3e465b357e9851..3197634c2d97f0 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>
 
@@ -19,7 +19,9 @@
 #include "test_iterators.h"
 
 using RangeEndT = decltype(std::ranges::end);
+#if TEST_STD_VER < 23
 using RangeCEndT = decltype(std::ranges::cend);
+#endif // TEST_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 TEST_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 // TEST_STD_VER < 23
 
 struct Incomplete;
 static_assert(!std::is_invocable_v<RangeEndT, Incomplete(&&)[]>);
 static_assert(!std::is_invocable_v<RangeEndT, Incomplete(&&)[42]>);
+#if TEST_STD_VER < 23
 static_assert(!std::is_invocable_v<RangeCEndT, Incomplete(&&)[]>);
 static_assert(!std::is_invocable_v<RangeCEndT, Incomplete(&&)[42]>);
+#endif // TEST_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 TEST_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 // TEST_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 TEST_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 // TEST_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 TEST_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 // TEST_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 TEST_STD_VER < 23
 static_assert(!std::is_invocable_v<RangeCEndT, NonConstEndMember &>);
 static_assert(!std::is_invocable_v<RangeCEndT, NonConstEndMember const&>);
+#endif // TEST_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 TEST_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 // TEST_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 TEST_STD_VER < 23
 static_assert( std::is_invocable_v<RangeCEndT, EndFunction const&>);
 static_assert( std::is_invocable_v<RangeCEndT, EndFunction &>);
+#endif // TEST_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 TEST_STD_VER < 23
 static_assert(!std::is_invocable_v<RangeCEndT, EndFunctionByValue>);
+#endif // TEST_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 TEST_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 // TEST_STD_VER < 23
 
   return true;
 }
 
 
 ASSERT_NOEXCEPT(std::ranges::end(std::declval<int (&)[10]>()));
+#if TEST_STD_VER < 23
 ASSERT_NOEXCEPT(std::ranges::cend(std::declval<int (&)[10]>()));
+#endif // TEST_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 TEST_STD_VER < 23
 static_assert(noexcept(std::ranges::cend(ntme)));
+#endif // TEST_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 TEST_STD_VER < 23
 static_assert(noexcept(std::ranges::cend(ntae)));
+#endif // TEST_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 TEST_STD_VER < 23
 static_assert(!noexcept(std::ranges::cend(ntmerr)));
+#endif // TEST_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 TEST_STD_VER < 23
 static_assert(noexcept(std::ranges::cend(erar)));
+#endif // TEST_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 TEST_STD_VER < 23
 static_assert(!std::is_invocable_v<RangeCEndT, Holder<Incomplete>*>);
 static_assert(!std::is_invocable_v<RangeCEndT, Holder<Incomplete>*&>);
+#endif // TEST_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 3997f38efd0298..7919b84d2b35a0 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 TEST_STD_VER < 23
 using RangeCRBeginT = decltype(std::ranges::crbegin);
+#endif // TEST_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 TEST_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 // TEST_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 TEST_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 // TEST_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 TEST_STD_VER < 23
 LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeCRBeginT, Incomplete(&)[]>);
 LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeCRBeginT, const Incomplete(&)[]>);
+#endif // TEST_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 TEST_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 // TEST_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 TEST_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 // TEST_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(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 TEST_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 // TEST_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 TEST_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 // TEST_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 TEST_STD_VER < 23
 static_assert(!std::is_invocable_v<RangeCRBeginT, NonConstRBeginMember &>);
 static_assert(!std::is_invocable_v<RangeCRBeginT, NonConstRBeginMember const&>);
+#endif // TEST_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 TEST_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 // TEST_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 TEST_STD_VER < 23
 static_assert( std::is_invocable_v<RangeCRBeginT, RBeginFunction const&>);
 static_assert( std::is_invocable_v<RangeCRBeginT, RBeginFunction &>);
+#endif // TEST_STD_VER < 23
 
 struct RBeginFunctionReturnsInt {
   friend int rbegin(RBeginFunctionReturnsInt const&);
@@ -217,7 +234,9 @@ static_assert(!std::is_invocable_v<RangeRBeginT, RBeginFunctionReturnsPtrConvert
 struct RBeginFunctionByValue {
   friend constexpr int *rbegin(RBeginFunctionByValue) { return globalBuff + 1; }
 };
+#if TEST_STD_VER < 23
 static_assert(!std::is_invocable_v<RangeCRBeginT, RBeginFunctionByValue>);
+#endif // TEST_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 TEST_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 // TEST_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 TEST_STD_VER < 23
 static_assert( std::is_invocable_v<RangeCRBeginT, MemberBeginEnd const&>);
+#endif // TEST_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 TEST_STD_VER < 23
 static_assert( std::is_invocable_v<RangeCRBeginT, FunctionBeginEnd const&>);
+#endif // TEST_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 TEST_STD_VER < 23
 static_assert( std::is_invocable_v<RangeCRBeginT, MemberBeginFunctionEnd const&>);
+#endif // TEST_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 TEST_STD_VER < 23
 static_assert( std::is_invocable_v<RangeCRBeginT, FunctionBeginMemberEnd const&>);
+#endif // TEST_STD_VER < 23
 
 struct MemberBeginEndDifferentTypes {
   bidirectional_iterator<int*> begin();
   bidirectional_iterator<const int*> end();
 };
 static_assert(!std::is_invocable_v<RangeRBeginT, MemberBeginEndDifferentTypes&>);
+#if TEST_STD_VER < 23
 static_assert(!std::is_invocable_v<RangeCRBeginT, MemberBeginEndDifferentTypes&>);
+#endif // TEST_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 TEST_STD_VER < 23
 static_assert(!std::is_invocable_v<RangeCRBeginT, FunctionBeginEndDifferentTypes&>);
+#endif // TEST_STD_VER < 23
 
 struct MemberBeginEndForwardIterators {
   forward_iterator<int*> begin();
   forward_iterator<int*> end();
 };
 static_assert(!std::is_invocable_v<RangeRBeginT, MemberBeginEndForwardIterators&>);
+#if TEST_STD_VER < 23
 static_assert(!std::is_invocable_v<RangeCRBeginT, MemberBeginEndForwardIterators&>);
+#endif // TEST_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 TEST_STD_VER < 23
 static_assert(!std::is_invocable_v<RangeCRBeginT, FunctionBeginEndForwardIterators&>);
+#endif // TEST_STD_VER < 23
 
 struct MemberBeginOnly {
   bidirectional_iterator<int*> begin() const;
 };
 static_assert(!std::is_invocable_v<RangeRBeginT, MemberBeginOnly&>);
+#if TEST_STD_VER < 23
 static_assert(!std::is_invocable_v<RangeCRBeginT, MemberBeginOnly&>);
+#endif // TEST_STD_VER < 23
 
 struct FunctionBeginOnly {
   friend bidirectional_iterator<int*> begin(FunctionBeginOnly&);
 };
 static_assert(!std::is_invocable_v<RangeRBeginT, FunctionBeginOnly&>);
+#if TEST_STD_VER < 23
 static_assert(!std::is_invocable_v<RangeCRBeginT, FunctionBeginOnly&>);
+#endif // TEST_STD_VER < 23
 
 struct MemberEndOnly {
   bidirectional_iterator<int*> end() const;
 };
 static_assert(!std::is_invocable_v<RangeRBeginT, MemberEndOnly&>);
+#if TEST_STD_VER < 23
 static_assert(!std::is_invocable_v<RangeCRBeginT, MemberEndOnly&>);
+#endif // TEST_STD_VER < 23
 
 struct FunctionEndOnly {
   friend bidirectional_iterator<int*> end(FunctionEndOnly&);
 };
 static_assert(!std::is_invocable_v<RangeRBeginT, FunctionEndOnly&>);
+#if TEST_STD_VER < 23
 static_assert(!std::is_invocable_v<RangeCRBeginT, FunctionEndOnly&>);
+#endif // TEST_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 TEST_STD_VER < 23
+static_assert(std::is_invocable_v<RangeCRBeginT, MemberBeginAndRBegin&>);
 static_assert( std::same_as<std::invoke_result_t<RangeCRBeginT, MemberBeginAndRBegin&>, int*>);
+#endif // TEST_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 TEST_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 // TEST_STD_VER < 23
 
   return true;
 }
 
 
 ASSERT_NOEXCEPT(std::ranges::rbegin(std::declval<int (&)[10]>()));
+#if TEST_STD_VER < 23
 ASSERT_NOEXCEPT(std::ranges::crbegin(std::declval<int (&)[10]>()));
+#endif // TEST_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 TEST_STD_VER < 23
 static_assert(noexcept(std::ranges::crbegin(ntmb)));
+#endif // TEST_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 TEST_STD_VER < 23
 static_assert(noexcept(std::ranges::crbegin(ntab)));
+#endif // TEST_STD_VER < 23
 
 struct NoThrowMemberRBeginReturnsRef {
   ThrowingIterator<int>& rbegin() const noexcept; // auto(t.rbegin()) may throw
 } ntmbrr;
 static_assert(!noexcept(std::ranges::rbegin(ntmbrr)));
+#if TEST_STD_VER < 23
 static_assert(!noexcept(std::ranges::crbegin(ntmbrr)));
+#endif // TEST_STD_VER < 23
 
 struct RBeginReturnsArrayRef {
     auto rbegin() const noexcept -> int(&)[10];
 } brar;
 static_assert(noexcept(std::ranges::rbegin(brar)));
+#if TEST_STD_VER < 23
 static_assert(noexcept(std::ranges::crbegin(brar)));
+#endif // TEST_STD_VER < 23
 
 struct NoThrowBeginThrowingEnd {
   int* begin() const noexcept;
   int* end() const;
 } ntbte;
 static_assert(!noexcept(std::ranges::rbegin(ntbte)));
+#if TEST_STD_VER < 23
 static_assert(!noexcept(std::ranges::crbegin(ntbte)));
+#endif // TEST_STD_VER < 23
 
 struct NoThrowEndThrowingBegin {
   int* begin() const;
   int* end() const noexcept;
 } ntetb;
 static_assert(noexcept(std::ranges::rbegin(ntetb)));
+#if TEST_STD_VER < 23
 static_assert(noexcept(std::ranges::crbegin(ntetb)));
+#endif // TEST_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 TEST_STD_VER < 23
 static_assert(!std::is_invocable_v<RangeCRBeginT, Holder<Incomplete>*>);
 static_assert(!std::is_invocable_v<RangeCRBeginT, Holder<Incomplete>*&>);
+#endif // TEST_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 f5f59edf19393f..adb150461e044b 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 TEST_STD_VER < 23
 using RangeCREndT = decltype(std::ranges::crend);
+#endif // TEST_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 TEST_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 // TEST_STD_VER < 23
 
 struct Incomplete;
 static_assert(!std::is_invocable_v<RangeREndT, Incomplete(&&)[]>);
 static_assert(!std::is_invocable_v<RangeREndT, Incomplete(&&)[42]>);
+#if TEST_STD_VER < 23
 static_assert(!std::is_invocable_v<RangeCREndT, Incomplete(&&)[]>);
 static_assert(!std::is_invocable_v<RangeCREndT, Incomplete(&&)[42]>);
+#endif // TEST_STD_VER < 23
 
 struct REndMember {
   int x;
@@ -49,50 +55,50 @@ 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 TEST_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 // TEST_STD_VER < 23
 
 constexpr bool testReturnTypes() {
-  {
-    int *x[2];
-    ASSERT_SAME_TYPE(decltype(std::ranges::rend(x)), std::reverse_iterator<int**>);
-    ASSERT_SAME_TYPE(decltype(std::ranges::crend(x)), std::reverse_iterator<int* const*>);
-  }
-
-  {
-    int x[2][2];
-    ASSERT_SAME_TYPE(decltype(std::ranges::rend(x)), std::reverse_iterator<int(*)[2]>);
-    ASSERT_SAME_TYPE(decltype(std::ranges::crend(x)), std::reverse_iterator<const int(*)[2]>);
-  }
-
-  {
-    struct Different {
-      char* rbegin();
-      sentinel_wrapper<char*>& rend();
-      short* rbegin() const;
-      sentinel_wrapper<short*>& rend() const;
-    } x;
-    ASSERT_SAME_TYPE(decltype(std::ranges::rend(x)), sentinel_wrapper<char*>);
-    ASSERT_SAME_TYPE(decltype(std::ranges::crend(x)), sentinel_wrapper<short*>);
-  }
+  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 TEST_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 // TEST_STD_VER < 23
 
   return true;
 }
 
 constexpr bool testArray() {
   int a[2];
-  assert(std::ranges::rend(a).base() == a);
-  assert(std::ranges::crend(a).base() == a);
-
   int b[2][2];
-  assert(std::ranges::rend(b).base() == b);
-  assert(std::ranges::crend(b).base() == b);
-
   REndMember c[2];
+
+  assert(std::ranges::rend(a).base() == a);
+  assert(std::ranges::rend(b).base() == b);
   assert(std::ranges::rend(c).base() == c);
+
+#if TEST_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 // TEST_STD_VER < 23
 
   return true;
 }
@@ -130,8 +136,10 @@ struct NonConstREndMember {
 };
 static_assert( std::is_invocable_v<RangeREndT,  NonConstREndMember &>);
 static_assert(!std::is_invocable_v<RangeREndT,  NonConstREndMember const&>);
+#if TEST_STD_VER < 23
 static_assert(!std::is_invocable_v<RangeCREndT, NonConstREndMember &>);
 static_assert(!std::is_invocable_v<RangeCREndT, NonConstREndMember const&>);
+#endif // TEST_STD_VER < 23
 
 struct EnabledBorrowingREndMember {
   constexpr int* rbegin() const { return nullptr; }
@@ -163,24 +171,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 TEST_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 // TEST_STD_VER < 23
 
   return true;
 }
@@ -197,8 +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 TEST_STD_VER < 23
 static_assert( std::is_invocable_v<RangeCREndT, REndFunction const&>);
 static_assert( std::is_invocable_v<RangeCREndT, REndFunction &>);
+#endif // TEST_STD_VER < 23
 
 struct REndFunctionReturnsInt {
   friend constexpr int rbegin(REndFunctionReturnsInt const&);
@@ -233,7 +243,9 @@ struct REndFunctionByValue {
   friend constexpr int* rbegin(REndFunctionByValue) { return nullptr; }
   friend constexpr int* rend(REndFunctionByValue) { return &globalBuff[1]; }
 };
+#if TEST_STD_VER < 23
 static_assert(!std::is_invocable_v<RangeCREndT, REndFunctionByValue>);
+#endif // TEST_STD_VER < 23
 
 struct REndFunctionEnabledBorrowing {
   friend constexpr int* rbegin(REndFunctionEnabledBorrowing) { return nullptr; }
@@ -269,47 +281,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 TEST_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 // TEST_STD_VER < 23
 
   return true;
 }
@@ -325,7 +335,9 @@ struct MemberBeginEnd {
 };
 static_assert( std::is_invocable_v<RangeREndT, MemberBeginEnd&>);
 static_assert( std::is_invocable_v<RangeREndT, MemberBeginEnd const&>);
+#if TEST_STD_VER < 23
 static_assert( std::is_invocable_v<RangeCREndT, MemberBeginEnd const&>);
+#endif // TEST_STD_VER < 23
 
 struct FunctionBeginEnd {
   int b, e;
@@ -343,7 +355,9 @@ struct FunctionBeginEnd {
 };
 static_assert( std::is_invocable_v<RangeREndT, FunctionBeginEnd&>);
 static_assert( std::is_invocable_v<RangeREndT, FunctionBeginEnd const&>);
+#if TEST_STD_VER < 23
 static_assert( std::is_invocable_v<RangeCREndT, FunctionBeginEnd const&>);
+#endif // TEST_STD_VER < 23
 
 struct MemberBeginFunctionEnd {
   int b, e;
@@ -359,7 +373,9 @@ struct MemberBeginFunctionEnd {
 };
 static_assert( std::is_invocable_v<RangeREndT, MemberBeginFunctionEnd&>);
 static_assert( std::is_invocable_v<RangeREndT, MemberBeginFunctionEnd const&>);
+#if TEST_STD_VER < 23
 static_assert( std::is_invocable_v<RangeCREndT, MemberBeginFunctionEnd const&>);
+#endif // TEST_STD_VER < 23
 
 struct FunctionBeginMemberEnd {
   int b, e;
@@ -375,59 +391,77 @@ struct FunctionBeginMemberEnd {
 };
 static_assert( std::is_invocable_v<RangeREndT, FunctionBeginMemberEnd&>);
 static_assert( std::is_invocable_v<RangeREndT, FunctionBeginMemberEnd const&>);
+#if TEST_STD_VER < 23
 static_assert( std::is_invocable_v<RangeCREndT, FunctionBeginMemberEnd const&>);
+#endif // TEST_STD_VER < 23
 
 struct MemberBeginEndDifferentTypes {
   bidirectional_iterator<int*> begin();
   bidirectional_iterator<const int*> end();
 };
 static_assert(!std::is_invocable_v<RangeREndT, MemberBeginEndDifferentTypes&>);
+#if TEST_STD_VER < 23
 static_assert(!std::is_invocable_v<RangeCREndT, MemberBeginEndDifferentTypes&>);
+#endif // TEST_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 TEST_STD_VER < 23
 static_assert(!std::is_invocable_v<RangeCREndT, FunctionBeginEndDifferentTypes&>);
+#endif // TEST_STD_VER < 23
 
 struct MemberBeginEndForwardIterators {
   forward_iterator<int*> begin();
   forward_iterator<int*> end();
 };
 static_assert(!std::is_invocable_v<RangeREndT, MemberBeginEndForwardIterators&>);
+#if TEST_STD_VER < 23
 static_assert(!std::is_invocable_v<RangeCREndT, MemberBeginEndForwardIterators&>);
+#endif // TEST_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 TEST_STD_VER < 23
 static_assert(!std::is_invocable_v<RangeCREndT, FunctionBeginEndForwardIterators&>);
+#endif // TEST_STD_VER < 23
 
 struct MemberBeginOnly {
   bidirectional_iterator<int*> begin() const;
 };
 static_assert(!std::is_invocable_v<RangeREndT, MemberBeginOnly&>);
+#if TEST_STD_VER < 23
 static_assert(!std::is_invocable_v<RangeCREndT, MemberBeginOnly&>);
+#endif // TEST_STD_VER < 23
 
 struct FunctionBeginOnly {
   friend bidirectional_iterator<int*> begin(FunctionBeginOnly&);
 };
 static_assert(!std::is_invocable_v<RangeREndT, FunctionBeginOnly&>);
+#if TEST_STD_VER < 23
 static_assert(!std::is_invocable_v<RangeCREndT, FunctionBeginOnly&>);
+#endif // TEST_STD_VER < 23
 
 struct MemberEndOnly {
   bidirectional_iterator<int*> end() const;
 };
 static_assert(!std::is_invocable_v<RangeREndT, MemberEndOnly&>);
+#if TEST_STD_VER < 23
 static_assert(!std::is_invocable_v<RangeCREndT, MemberEndOnly&>);
+#endif // TEST_STD_VER < 23
 
 struct FunctionEndOnly {
   friend bidirectional_iterator<int*> end(FunctionEndOnly&);
 };
 static_assert(!std::is_invocable_v<RangeREndT, FunctionEndOnly&>);
+#if TEST_STD_VER < 23
 static_assert(!std::is_invocable_v<RangeCREndT, FunctionEndOnly&>);
+#endif // TEST_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 +472,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 TEST_STD_VER < 23
+static_assert(std::is_invocable_v<RangeCREndT, MemberBeginAndRBegin&>);
 static_assert( std::same_as<std::invoke_result_t<RangeCREndT, MemberBeginAndRBegin&>, int*>);
+#endif // TEST_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 TEST_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 // TEST_STD_VER < 23
 
   return true;
 }
 
 
 ASSERT_NOEXCEPT(std::ranges::rend(std::declval<int (&)[10]>()));
+#if TEST_STD_VER < 23
 ASSERT_NOEXCEPT(std::ranges::crend(std::declval<int (&)[10]>()));
+#endif // TEST_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 TEST_STD_VER < 23
 static_assert(noexcept(std::ranges::crend(ntmre)));
+#endif // TEST_STD_VER < 23
 
 struct NoThrowADLREnd {
   ThrowingIterator<int> rbegin() const;
@@ -492,43 +533,55 @@ struct NoThrowADLREnd {
   friend ThrowingIterator<int> rend(const NoThrowADLREnd&) noexcept;
 } ntare;
 static_assert(noexcept(std::ranges::rend(ntare)));
+#if TEST_STD_VER < 23
 static_assert(noexcept(std::ranges::crend(ntare)));
+#endif // TEST_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 TEST_STD_VER < 23
 static_assert(!noexcept(std::ranges::crend(ntmrerr)));
+#endif // TEST_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 TEST_STD_VER < 23
 static_assert(noexcept(std::ranges::crend(rerar)));
+#endif // TEST_STD_VER < 23
 
 struct NoThrowBeginThrowingEnd {
   int* begin() const noexcept;
   int* end() const;
 } ntbte;
 static_assert(noexcept(std::ranges::rend(ntbte)));
+#if TEST_STD_VER < 23
 static_assert(noexcept(std::ranges::crend(ntbte)));
+#endif // TEST_STD_VER < 23
 
 struct NoThrowEndThrowingBegin {
   int* begin() const;
   int* end() const noexcept;
 } ntetb;
 static_assert(!noexcept(std::ranges::rend(ntetb)));
+#if TEST_STD_VER < 23
 static_assert(!noexcept(std::ranges::crend(ntetb)));
+#endif // TEST_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 TEST_STD_VER < 23
 static_assert(!std::is_invocable_v<RangeCREndT, Holder<Incomplete>*>);
 static_assert(!std::is_invocable_v<RangeCREndT, Holder<Incomplete>*&>);
+#endif // TEST_STD_VER < 23
 
 int main(int, char**) {
   static_assert(testReturnTypes());
diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.const/adaptor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.const/adaptor.pass.cpp
new file mode 100644
index 00000000000000..a4bee123857337
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as.const/adaptor.pass.cpp
@@ -0,0 +1,156 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// std::views::as_const
+
+#include <array>
+#include <cassert>
+#include <functional>
+#include <ranges>
+#include <vector>
+
+#include "test_iterators.h"
+
+template <class View, class T>
+concept HasPipe = requires {
+  { std::declval<View>() | std::declval<T>() };
+};
+
+struct DefaultConstructibleView : std::ranges::view_base {
+  int i_;
+  int* begin();
+  int* end();
+};
+struct NoView {};
+
+static_assert(std::is_invocable_v<decltype(std::views::as_const), DefaultConstructibleView>);
+static_assert(!std::is_invocable_v<decltype(std::views::as_const)>);
+static_assert(!std::is_invocable_v<decltype(std::views::as_const), NoView>);
+static_assert(HasPipe<DefaultConstructibleView&, decltype(std::views::as_const)>);
+static_assert(HasPipe<int (&)[10], decltype(std::views::as_const)>);
+static_assert(!HasPipe<int (&&)[10], decltype(std::views::as_const)>);
+static_assert(!HasPipe<NoView, decltype(std::views::as_const)>);
+static_assert(std::is_same_v<decltype(std::views::as_const), decltype(std::ranges::views::as_const)>);
+
+struct const_iterator_range {
+  constexpr std::const_iterator<int*> begin() const { return {}; }
+  constexpr std::const_iterator<int*> end() const { return {}; }
+};
+static_assert(!std::ranges::view<const_iterator_range>);
+static_assert(std::ranges::range<const_iterator_range>);
+
+constexpr bool test() {
+  // Let E be an expression, let T be decltype((E)), and let U be remove_cvref_t<T>.
+  // The expression views::as_const(E) is expression-equivalent to:
+
+  // - If views::all_t<T> models constant_range, then views::all(E).
+  {
+    [[maybe_unused]] std::same_as<std::views::all_t<const_iterator_range>> decltype(auto) view =
+        std::views::as_const(const_iterator_range{});
+  }
+  {
+    // ambiguous with empty_view case
+    [[maybe_unused]] std::same_as<std::views::all_t<std::ranges::empty_view<const int>>> decltype(auto) view =
+        std::views::empty<const int> | std::views::as_const;
+  }
+  {
+    // ambiguous with span case
+    int a[3] = {};
+    [[maybe_unused]] std::same_as<std::views::all_t<std::span<const int>>> decltype(auto) view1 =
+        std::span<const int>(a) | std::views::as_const;
+    [[maybe_unused]] std::same_as<std::views::all_t<std::span<const int, 3>>> decltype(auto) view2 =
+        std::span<const int, 3>(a) | std::views::as_const;
+  }
+  {
+    // ambiguous with ref_view case
+    std::array<int, 3> a                              = {};
+    std::ranges::ref_view<const std::array<int, 3>> r = a;
+    [[maybe_unused]] std::same_as<std::ranges::ref_view<const std::array<int, 3>>> decltype(auto) view =
+        r | std::views::as_const;
+  }
+  {
+    // ambiguous with constant_range case
+    std::array<const int, 3> a = {};
+    [[maybe_unused]] std::same_as<std::ranges::ref_view<std::array<const int, 3>>> decltype(auto) view =
+        a | std::views::as_const;
+  }
+
+  // - Otherwise, if U denotes empty_view<X> for some type X, then auto(views::empty<const X>).
+  {
+    [[maybe_unused]] std::same_as<std::ranges::empty_view<const int>> decltype(auto) view =
+        std::views::empty<int> | std::views::as_const;
+  }
+
+  // - Otherwise, if U denotes span<X, Extent> for some type X and some extent Extent, then span<const X, Extent>(E).
+  {
+    int a[3]                                               = {};
+    std::same_as<std::span<const int>> decltype(auto) view = std::span<int>(a) | std::views::as_const;
+    assert(std::to_address(view.begin()) == a);
+    assert(std::to_address(view.end()) == a + 3);
+  }
+  {
+    int a[3]                                                  = {};
+    std::same_as<std::span<const int, 3>> decltype(auto) view = std::span<int, 3>(a) | std::views::as_const;
+    assert(std::to_address(view.begin()) == a);
+    assert(std::to_address(view.end()) == a + 3);
+  }
+
+  // - Otherwise, if U denotes ref_view<X> for some type X and const X models constant_range, then ref_view(static_cast<const X&>(E.base())).
+  {
+    std::array<int, 3> a                        = {};
+    std::ranges::ref_view<std::array<int, 3>> r = a;
+    [[maybe_unused]] std::same_as<std::ranges::ref_view<const std::array<int, 3>>> decltype(auto) view =
+        r | std::views::as_const;
+  }
+
+  // - Otherwise, if E is an lvalue, const U models constant_range, and U does not model view, then ref_view(static_cast<const U&>(E)).
+  {
+    std::array<int, 3> a = {};
+    [[maybe_unused]] std::same_as<std::ranges::ref_view<const std::array<int, 3>>> decltype(auto) view =
+        a | std::views::as_const;
+  }
+
+  // - Otherwise, as_const_view(E).
+  { // view | views::as_const
+    DefaultConstructibleView v{{}, 3};
+    std::same_as<std::ranges::as_const_view<DefaultConstructibleView>> decltype(auto) view = v | std::views::as_const;
+    assert(view.base().i_ == 3);
+  }
+
+  { // adaptor | views::as_const
+    DefaultConstructibleView v{{}, 3};
+    const auto partial = std::views::transform(std::identity{}) | std::views::as_const;
+    std::same_as<std::ranges::as_const_view<
+        std::ranges::transform_view<DefaultConstructibleView, std::identity>>> decltype(auto) view = partial(v);
+    assert(view.base().base().i_ == 3);
+  }
+
+  { // views::as_const | adaptor
+    DefaultConstructibleView v{{}, 3};
+    const auto partial = std::views::as_const | std::views::transform(std::identity{});
+    std::same_as<std::ranges::transform_view<std::ranges::as_const_view<DefaultConstructibleView>,
+                                             std::identity>> decltype(auto) view = partial(v);
+    assert(view.base().base().i_ == 3);
+  }
+
+  { // range | views::as_const
+    [[maybe_unused]] std::same_as<std::ranges::as_const_view<std::views::all_t<std::vector<int>>>> decltype(auto) view =
+        std::vector<int>{} | std::views::as_const;
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.const/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.const/base.pass.cpp
new file mode 100644
index 00000000000000..c842c59f5ad66f
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as.const/base.pass.cpp
@@ -0,0 +1,78 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// constexpr V base() const& requires copy_constructible<V> { return base_; }
+// constexpr V base() && { return std::move(base_); }
+
+#include <cassert>
+#include <ranges>
+#include <utility>
+
+#include "MoveOnly.h"
+
+struct SimpleView : std::ranges::view_base {
+  int i;
+  int* begin() const;
+  int* end() const;
+};
+
+struct MoveOnlyView : SimpleView {
+  MoveOnly m;
+};
+
+template <class T>
+concept HasBase = requires(T&& t) { std::forward<T>(t).base(); };
+
+static_assert(HasBase<std::ranges::as_const_view<SimpleView> const&>);
+static_assert(HasBase<std::ranges::as_const_view<SimpleView>&&>);
+
+static_assert(!HasBase<std::ranges::as_const_view<MoveOnlyView> const&>);
+static_assert(HasBase<std::ranges::as_const_view<MoveOnlyView>&&>);
+
+constexpr bool test() {
+  { // const &
+    const std::ranges::as_const_view<SimpleView> view(SimpleView{{}, 5});
+    std::same_as<SimpleView> decltype(auto) v = view.base();
+    assert(v.i == 5);
+  }
+
+  { // &
+    std::ranges::as_const_view<SimpleView> view(SimpleView{{}, 5});
+    std::same_as<SimpleView> decltype(auto) v = view.base();
+    assert(v.i == 5);
+  }
+
+  { // &&
+    std::ranges::as_const_view<SimpleView> view(SimpleView{{}, 5});
+    std::same_as<SimpleView> decltype(auto) v = std::move(view).base();
+    assert(v.i == 5);
+  }
+
+  { // const &&
+    const std::ranges::as_const_view<SimpleView> view(SimpleView{{}, 5});
+    std::same_as<SimpleView> decltype(auto) v = std::move(view).base();
+    assert(v.i == 5);
+  }
+
+  { // move only
+    std::ranges::as_const_view<MoveOnlyView> view(MoveOnlyView{{}, 5});
+    std::same_as<MoveOnlyView> decltype(auto) v = std::move(view).base();
+    assert(v.m.get() == 5);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.const/begin.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.const/begin.pass.cpp
new file mode 100644
index 00000000000000..809a08bce2fcca
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as.const/begin.pass.cpp
@@ -0,0 +1,124 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// constexpr auto begin()
+// constexpr auto begin() const
+
+#include <array>
+#include <cassert>
+#include <concepts>
+#include <ranges>
+
+#include "test_iterators.h"
+
+struct SimpleView : std::ranges::view_base {
+  int* begin() const;
+  int* end() const;
+};
+
+struct NonSimpleView : std::ranges::view_base {
+  char* begin();
+  char* end();
+  int* begin() const;
+  int* end() const;
+};
+
+struct NonConstView : std::ranges::view_base {
+  char* begin();
+  char* end();
+};
+
+template <class T>
+concept HasBegin = requires(T t) { t.begin(); };
+
+static_assert(HasBegin<std::ranges::as_const_view<SimpleView>>);
+static_assert(HasBegin<const std::ranges::as_const_view<SimpleView>>);
+static_assert(HasBegin<std::ranges::as_const_view<NonSimpleView>>);
+static_assert(HasBegin<const std::ranges::as_const_view<NonSimpleView>>);
+static_assert(HasBegin<std::ranges::as_const_view<NonConstView>>);
+static_assert(!HasBegin<const std::ranges::as_const_view<NonConstView>>);
+
+template <class Iter, class Sent>
+constexpr void test_range() {
+  int a[] = {1, 2};
+  std::ranges::subrange range(Iter(std::begin(a)), Sent(Iter(std::end(a))));
+  std::ranges::as_const_view view(std::move(range));
+  std::same_as<std::const_iterator<Iter>> decltype(auto) iter = view.begin();
+  assert(base(iter.base()) == std::begin(a));
+}
+
+template <class Iter, class Sent>
+class WrapRange {
+  Iter iter_;
+  Sent sent_;
+
+public:
+  constexpr WrapRange(Iter iter, Sent sent) : iter_(std::move(iter)), sent_(std::move(sent)) {}
+
+  constexpr Iter begin() const { return iter_; }
+  constexpr Sent end() const { return sent_; }
+};
+
+template <class Iter, class Sent>
+WrapRange(Iter, Sent) -> WrapRange<Iter, Sent>;
+
+template <class Iter, class Sent>
+constexpr void test_const_range() {
+  int a[]    = {1, 2};
+  auto range = WrapRange{Iter(a), Sent(Iter(a + 2))};
+  const std::ranges::as_const_view view(std::views::all(range));
+  std::same_as<Iter> decltype(auto) iter = view.begin();
+  assert(base(iter) == std::begin(a));
+}
+
+struct const_pointer_view : std::ranges::view_base {
+  constexpr const int* begin() const { return {}; }
+  constexpr const int* end() const { return {}; }
+};
+struct const_iterator_view : std::ranges::view_base {
+  constexpr std::const_iterator<int*> begin() const { return {}; }
+  constexpr std::const_iterator<int*> end() const { return {}; }
+};
+
+constexpr bool test() {
+  types::for_each(types::cpp20_input_iterator_list<int*>{}, []<class Iter> {
+    if constexpr (std::sentinel_for<Iter, Iter>)
+      test_range<Iter, Iter>();
+    test_range<Iter, sentinel_wrapper<Iter>>();
+    test_range<Iter, sized_sentinel<Iter>>();
+  });
+
+  types::for_each(types::forward_iterator_list<const int*>{}, []<class Iter> {
+    test_const_range<Iter, Iter>();
+    test_const_range<Iter, sentinel_wrapper<Iter>>();
+    test_const_range<Iter, sized_sentinel<Iter>>();
+  });
+
+  // check that with a const_iterator begin() doesn't return std::const_iterator<const_iterator>
+  {
+    std::ranges::as_const_view view{const_pointer_view{}};
+    std::same_as<const int*> decltype(auto) it = view.begin();
+    assert(it == nullptr);
+  }
+  {
+    std::ranges::as_const_view view{const_iterator_view{}};
+    std::same_as<std::const_iterator<int*>> decltype(auto) it = view.begin();
+    assert(it == std::const_iterator<int*>{});
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.const/ctad.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.const/ctad.compile.pass.cpp
new file mode 100644
index 00000000000000..063b4bcd2c9941
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as.const/ctad.compile.pass.cpp
@@ -0,0 +1,22 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+#include <ranges>
+#include <vector>
+#include <utility>
+
+static_assert(std::is_same_v<decltype(std::ranges::as_const_view(std::vector<int>{})),
+                             std::ranges::as_const_view<std::views::all_t<std::vector<int>>>>);
+
+static_assert(std::is_same_v<decltype(std::ranges::as_const_view(std::declval<std::vector<int>&>())),
+                             std::ranges::as_const_view<std::views::all_t<std::vector<int>&>>>);
+
+static_assert(std::is_same_v<decltype(std::ranges::as_const_view(std::ranges::empty_view<int>{})),
+                             std::ranges::as_const_view<std::ranges::empty_view<int>>>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.const/ctor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.const/ctor.pass.cpp
new file mode 100644
index 00000000000000..54384c9305d1ce
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as.const/ctor.pass.cpp
@@ -0,0 +1,57 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// std::ranges::as_const_view::as_const_view(...)
+
+#include <cassert>
+#include <ranges>
+#include <type_traits>
+#include <utility>
+#include <vector>
+
+struct DefaultConstructibleView : std::ranges::view_base {
+  int* begin() const;
+  int* end() const;
+
+  int i_ = 23;
+};
+
+struct NonDefaultConstructibleView : std::ranges::view_base {
+  NonDefaultConstructibleView(int i) : i_(i) {}
+
+  int* begin() const;
+  int* end() const;
+
+  int i_ = 23;
+};
+
+static_assert(!std::is_constructible_v<std::ranges::as_const_view<NonDefaultConstructibleView>>);
+static_assert(std::is_constructible_v<std::ranges::as_const_view<NonDefaultConstructibleView>, int>);
+static_assert(std::is_nothrow_constructible_v<std::ranges::as_const_view<DefaultConstructibleView>>);
+
+template <class T, class... Args>
+concept IsImplicitlyConstructible = requires(T val, Args... args) { val = {std::forward<Args>(args)...}; };
+
+static_assert(IsImplicitlyConstructible<std::ranges::as_const_view<DefaultConstructibleView>>);
+static_assert(!IsImplicitlyConstructible<std::ranges::as_const_view<NonDefaultConstructibleView>, int>);
+
+constexpr bool test() {
+  std::ranges::as_const_view<DefaultConstructibleView> view = {};
+  assert(view.base().i_ == 23);
+
+  return true;
+}
+
+int main(int, char**) {
+  static_assert(test());
+  test();
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.const/enable_borrowed_range.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.const/enable_borrowed_range.compile.pass.cpp
new file mode 100644
index 00000000000000..5086f6fdb50b2e
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as.const/enable_borrowed_range.compile.pass.cpp
@@ -0,0 +1,16 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+#include <ranges>
+#include <vector>
+
+static_assert(std::ranges::enable_borrowed_range<std::ranges::as_const_view<std::ranges::empty_view<int>>>);
+static_assert(std::ranges::enable_borrowed_range<std::ranges::as_const_view<std::views::all_t<std::vector<int>&>>>);
+static_assert(!std::ranges::enable_borrowed_range<std::ranges::as_const_view<std::views::all_t<std::vector<int>>>>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.const/end.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.const/end.pass.cpp
new file mode 100644
index 00000000000000..e7c7b16f74011d
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as.const/end.pass.cpp
@@ -0,0 +1,129 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// constexpr auto end()
+// constexpr auto end() const
+
+#include <array>
+#include <cassert>
+#include <ranges>
+
+#include "test_iterators.h"
+
+struct DefaultConstructibleView : std::ranges::view_base {
+  int* begin() const;
+  int* end() const;
+};
+
+struct NonConstCommonRange : std::ranges::view_base {
+  int* begin();
+  int* end();
+
+  int* begin() const;
+  sentinel_wrapper<int*> end() const;
+};
+
+struct NonConstView : std::ranges::view_base {
+  int* begin();
+  int* end();
+};
+
+template <class T>
+concept HasEnd = requires(T t) { t.end(); };
+
+static_assert(HasEnd<std::ranges::as_const_view<DefaultConstructibleView>>);
+static_assert(HasEnd<const std::ranges::as_const_view<DefaultConstructibleView>>);
+static_assert(HasEnd<std::ranges::as_const_view<NonConstView>>);
+static_assert(!HasEnd<const std::ranges::as_const_view<NonConstView>>);
+
+static_assert(std::is_same_v<decltype(std::declval<std::ranges::as_const_view<DefaultConstructibleView>>().end()),
+                             std::const_iterator<int*>>);
+static_assert(std::is_same_v<decltype(std::declval<const std::ranges::as_const_view<NonConstCommonRange>>().end()),
+                             std::const_sentinel<sentinel_wrapper<int*>>>);
+
+template <class Iter, class Sent>
+constexpr void test_range() {
+  int a[] = {1, 2};
+  std::ranges::subrange range(Iter(std::begin(a)), Sent(Iter(std::end(a))));
+  std::ranges::as_const_view view(std::move(range));
+  std::same_as<std::const_sentinel<Sent>> decltype(auto) iter = view.end();
+  assert(base(base(iter)) == std::end(a));
+}
+
+template <class Iter, class Sent>
+class WrapRange {
+  Iter iter_;
+  Sent sent_;
+
+public:
+  constexpr WrapRange(Iter iter, Sent sent) : iter_(std::move(iter)), sent_(std::move(sent)) {}
+
+  constexpr Iter begin() const { return iter_; }
+  constexpr Sent end() const { return sent_; }
+};
+
+template <class Iter, class Sent>
+WrapRange(Iter, Sent) -> WrapRange<Iter, Sent>;
+
+template <class Iter, class Sent, bool is_common>
+constexpr void test_const_range() {
+  int a[]    = {1, 2};
+  auto range = WrapRange{Iter(a), Sent(Iter(a + 2))};
+  const std::ranges::as_const_view view(std::move(range));
+  std::same_as<std::const_sentinel<Sent>> decltype(auto) iter = view.end();
+  assert(base(base(iter)) == std::end(a));
+}
+
+struct const_pointer_view : std::ranges::view_base {
+  constexpr const int* begin() const { return {}; }
+  constexpr const int* end() const { return {}; }
+};
+struct const_iterator_view : std::ranges::view_base {
+  constexpr std::const_iterator<int*> begin() const { return {}; }
+  constexpr std::const_iterator<int*> end() const { return {}; }
+};
+
+constexpr bool test() {
+  test_range<cpp17_input_iterator<int*>, sentinel_wrapper<cpp17_input_iterator<int*>>>();
+  test_range<cpp17_input_iterator<int*>, sized_sentinel<cpp17_input_iterator<int*>>>();
+  test_range<cpp20_input_iterator<int*>, sentinel_wrapper<cpp20_input_iterator<int*>>>();
+  test_range<cpp20_input_iterator<int*>, sized_sentinel<cpp20_input_iterator<int*>>>();
+
+  types::for_each(types::forward_iterator_list<int*>{}, []<class Iter> {
+    test_range<Iter, Iter>();
+    test_range<Iter, sentinel_wrapper<Iter>>();
+    test_range<Iter, sized_sentinel<Iter>>();
+  });
+
+  // check that with a const_iterator end() doesn't return std::const_iterator<const_iterator>
+  {
+    std::ranges::as_const_view view{const_pointer_view{}};
+    std::same_as<const int*> decltype(auto) it = view.end();
+    assert(it == nullptr);
+  }
+  {
+    std::ranges::as_const_view view{const_iterator_view{}};
+    std::same_as<std::const_iterator<int*>> decltype(auto) it = view.end();
+    assert(it == std::const_iterator<int*>{});
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+
+// gcc cannot have mutable member in constant expression
+#if !defined(TEST_COMPILER_GCC)
+  static_assert(test());
+#endif
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.const/size.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.const/size.pass.cpp
new file mode 100644
index 00000000000000..62f8282ddc0b6d
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as.const/size.pass.cpp
@@ -0,0 +1,80 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// constexpr auto size()
+// constexpr auto size() const
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+#include <cassert>
+#include <cstddef>
+#include <ranges>
+
+struct ConstSizedView : std::ranges::view_base {
+  bool* size_called;
+  int* begin() const;
+  int* end() const;
+
+  constexpr std::size_t size() const {
+    *size_called = true;
+    return 3;
+  }
+};
+
+struct SizedView : std::ranges::view_base {
+  bool* size_called;
+  int* begin() const;
+  int* end() const;
+
+  constexpr int size() {
+    *size_called = true;
+    return 5;
+  }
+};
+
+struct UnsizedView : std::ranges::view_base {
+  int* begin() const;
+  int* end() const;
+};
+
+template <class T>
+concept HasSize = requires(T v) { v.size(); };
+
+static_assert(HasSize<ConstSizedView>);
+static_assert(HasSize<const ConstSizedView>);
+static_assert(HasSize<SizedView>);
+static_assert(!HasSize<const SizedView>);
+static_assert(!HasSize<UnsizedView>);
+static_assert(!HasSize<const UnsizedView>);
+
+constexpr bool test() {
+  {
+    bool size_called = false;
+    std::ranges::as_const_view view(ConstSizedView{{}, &size_called});
+    std::same_as<std::size_t> auto size = view.size();
+    assert(size == 3);
+    assert(size_called);
+  }
+
+  {
+    bool size_called = false;
+    std::ranges::as_const_view view(SizedView{{}, &size_called});
+    std::same_as<int> auto size = view.size();
+    assert(size == 5);
+    assert(size_called);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.utility/view.interface/view.interface.pass.cpp b/libcxx/test/std/ranges/range.utility/view.interface/view.interface.pass.cpp
index 3f7c174d3fe48a..8c711cd7708622 100644
--- a/libcxx/test/std/ranges/range.utility/view.interface/view.interface.pass.cpp
+++ b/libcxx/test/std/ranges/range.utility/view.interface/view.interface.pass.cpp
@@ -35,8 +35,15 @@ using InputIter = cpp20_input_iterator<const int*>;
 
 struct InputRange : std::ranges::view_interface<InputRange> {
   int buff[8] = {0, 1, 2, 3, 4, 5, 6, 7};
+
   constexpr InputIter begin() const { return InputIter(buff); }
-  constexpr InputIter end() const { return InputIter(buff + 8); }
+  constexpr sentinel_wrapper<InputIter> end() const { return sentinel_wrapper(InputIter(buff + 8)); }
+};
+
+struct NonConstInputRange : std::ranges::view_interface<NonConstInputRange> {
+  int buff[8] = {0, 1, 2, 3, 4, 5, 6, 7};
+  constexpr InputIter begin() { return InputIter(buff); }
+  constexpr sentinel_wrapper<InputIter> end() { return sentinel_wrapper(InputIter(buff + 8)); }
 };
 
 struct SizedInputRange : std::ranges::view_interface<SizedInputRange> {
@@ -222,6 +229,19 @@ constexpr bool testEmpty() {
   return true;
 }
 
+#if TEST_STD_VER >= 23
+template <class T>
+concept ConstAccessorsInvocable = requires(T& t) {
+  t.cbegin();
+  t.cend();
+};
+
+static_assert(ConstAccessorsInvocable<InputRange>);
+static_assert(ConstAccessorsInvocable<const InputRange>);
+static_assert(ConstAccessorsInvocable<NonConstInputRange>);
+static_assert(!ConstAccessorsInvocable<const NonConstInputRange>);
+#endif // TEST_STD_VER >= 23
+
 template<class T>
 concept DataInvocable = requires (T const& obj) { obj.data(); };
 
diff --git a/libcxx/test/support/test_iterators.h b/libcxx/test/support/test_iterators.h
index 419f1d86730b85..3f70fa0f258368 100644
--- a/libcxx/test/support/test_iterators.h
+++ b/libcxx/test/support/test_iterators.h
@@ -511,6 +511,13 @@ template <class It>
 three_way_contiguous_iterator(It) -> three_way_contiguous_iterator<It>;
 #endif // TEST_STD_VER > 17
 
+#if TEST_STD_VER >= 23
+template <class Iter>
+TEST_CONSTEXPR Iter base(std::basic_const_iterator<Iter> i) {
+  return i.base();
+}
+#endif
+
 template <class Iter> // ADL base() for everything else (including pointers)
 TEST_CONSTEXPR Iter base(Iter i) { return i; }
 
diff --git a/libcxx/test/support/test_macros.h b/libcxx/test/support/test_macros.h
index 5d4c1a65cfafb2..e9b00ea93dfdb1 100644
--- a/libcxx/test/support/test_macros.h
+++ b/libcxx/test/support/test_macros.h
@@ -277,6 +277,12 @@ struct is_same<T, T> { enum {value = 1}; };
     static_assert((test_macros_detail::is_same<__VA_ARGS__>::value), \
                  "Types differ unexpectedly")
 
+#define ASSERT_RUNTIME_AND_CONSTEXPR(...)                                                                              \
+  do {                                                                                                                 \
+    assert((__VA_ARGS__));                                                                                             \
+    static_assert(__VA_ARGS__);                                                                                        \
+  } while (0)
+
 #ifndef TEST_HAS_NO_EXCEPTIONS
 #define TEST_THROW(...) throw __VA_ARGS__
 #else
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index 3bdd3adad15b4e..185111407e9421 100755
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -1001,11 +1001,9 @@ 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,
         },
         {
             "name": "__cpp_lib_ranges_as_rvalue",

>From 9c2f7ca3396d67d29f357f1379c4b0032c082995 Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Thu, 31 Oct 2024 22:40:25 +0800
Subject: [PATCH 2/5] Remove a mistakenly merged line in `module.modulemap`

---
 libcxx/include/module.modulemap | 1 -
 1 file changed, 1 deletion(-)

diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap
index d5be371636fb3d..227a6859b73007 100644
--- a/libcxx/include/module.modulemap
+++ b/libcxx/include/module.modulemap
@@ -1928,7 +1928,6 @@ module std [system] {
 
     module support {
       header "__thread/support.h"
-module std_private_iterator_const_iterator          [system] { header "__iterator/const_iterator.h" }
       export *
     }
     module support_impl {

>From 2601e34ad479d71712c873c16e957564b8fbcaae Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Fri, 1 Nov 2024 13:47:08 +0800
Subject: [PATCH 3/5] Missing inclusion and test skipping

---
 libcxx/include/__iterator/const_iterator.h    |  2 +
 libcxx/include/__ranges/access.h              |  1 +
 libcxx/include/__ranges/as_const_view.h       |  3 ++
 .../views/views.span/span.cons/copy.pass.cpp  |  8 +++-
 .../views/views.span/span.cons/span.pass.cpp  | 14 +++++-
 .../views/views.span/types.pass.cpp           | 47 +++++++++++--------
 6 files changed, 52 insertions(+), 23 deletions(-)

diff --git a/libcxx/include/__iterator/const_iterator.h b/libcxx/include/__iterator/const_iterator.h
index 4fcd76b4117dc6..b98d27fb7c7698 100644
--- a/libcxx/include/__iterator/const_iterator.h
+++ b/libcxx/include/__iterator/const_iterator.h
@@ -12,6 +12,7 @@
 
 #include <__compare/three_way_comparable.h>
 #include <__concepts/common_with.h>
+#include <__concepts/constructible.h>
 #include <__concepts/convertible_to.h>
 #include <__concepts/different_from.h>
 #include <__concepts/same_as.h>
@@ -29,6 +30,7 @@
 #include <__type_traits/integral_constant.h>
 #include <__type_traits/is_reference.h>
 #include <__type_traits/is_specialization.h>
+#include <__type_traits/remove_cvref.h>
 #include <__utility/forward.h>
 #include <__utility/move.h>
 
diff --git a/libcxx/include/__ranges/access.h b/libcxx/include/__ranges/access.h
index e71e5ce8f9eeb9..777db55d38f172 100644
--- a/libcxx/include/__ranges/access.h
+++ b/libcxx/include/__ranges/access.h
@@ -18,6 +18,7 @@
 #include <__memory/pointer_traits.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__type_traits/decay.h>
+#include <__type_traits/is_object.h>
 #include <__type_traits/is_pointer.h>
 #include <__type_traits/is_reference.h>
 #include <__type_traits/remove_cvref.h>
diff --git a/libcxx/include/__ranges/as_const_view.h b/libcxx/include/__ranges/as_const_view.h
index d2613ad939fd0d..d991f4641e532d 100644
--- a/libcxx/include/__ranges/as_const_view.h
+++ b/libcxx/include/__ranges/as_const_view.h
@@ -20,8 +20,11 @@
 #include <__ranges/ref_view.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
+#include <__type_traits/is_reference.h>
 #include <__type_traits/is_specialization.h>
 #include <__utility/auto_cast.h>
+#include <__utility/declval.h>
+#include <__utility/forward.h>
 #include <__utility/move.h>
 #include <__utility/pair.h>
 #include <cstddef>
diff --git a/libcxx/test/std/containers/views/views.span/span.cons/copy.pass.cpp b/libcxx/test/std/containers/views/views.span/span.cons/copy.pass.cpp
index d3990fd60a459a..3780e239a2207d 100644
--- a/libcxx/test/std/containers/views/views.span/span.cons/copy.pass.cpp
+++ b/libcxx/test/std/containers/views/views.span/span.cons/copy.pass.cpp
@@ -87,14 +87,17 @@ constexpr bool test_all() {
   test<volatile double>();
   test<const volatile double>();
 
-  // Note: Can't test non-fundamental types with volatile because we require `T*` to be indirectly_readable,
-  //       which isn't the case when T is volatile.
+  // Note: Can't test class types with volatile because we require `T*` to be indirectly_readable,
+  //       which isn't the case when T is volatile. See also LWG3813.
   test<Foo>();
   test<const Foo>();
 
   test<std::string>();
   test<const std::string>();
 
+#if defined(_LIBCPP_VERSION) && TEST_STD_VER < 23
+  // libc++ supports span<Incomplete> as an extension in C++20 mode,
+  // but the extension is incompatible with changes of span in C++23.
   // Regression test for https://github.com/llvm/llvm-project/issues/104496
   {
     struct Incomplete;
@@ -103,6 +106,7 @@ constexpr bool test_all() {
     assert(copy.data() == x.data());
     assert(copy.size() == x.size());
   }
+#endif
 
   return true;
 }
diff --git a/libcxx/test/std/containers/views/views.span/span.cons/span.pass.cpp b/libcxx/test/std/containers/views/views.span/span.cons/span.pass.cpp
index a1ac0dd11b2d1a..526b7384835144 100644
--- a/libcxx/test/std/containers/views/views.span/span.cons/span.pass.cpp
+++ b/libcxx/test/std/containers/views/views.span/span.cons/span.pass.cpp
@@ -87,11 +87,16 @@ TEST_CONSTEXPR_CXX20 void check() {
 }
 
 template <class T>
-TEST_CONSTEXPR_CXX20 void check_cvs() {
+constexpr void check_non_volatile() {
   check<T, T>();
 
   check<T const, T>();
   check<T const, T const>();
+}
+
+template <class T>
+constexpr void check_cvs() {
+  check_non_volatile<T>();
 
   check<T volatile, T>();
   check<T volatile, T volatile>();
@@ -104,12 +109,17 @@ TEST_CONSTEXPR_CXX20 void check_cvs() {
 
 struct A {};
 
-TEST_CONSTEXPR_CXX20 bool test() {
+constexpr bool test() {
   check_cvs<int>();
   check_cvs<long>();
   check_cvs<double>();
+#if TEST_STD_VER >= 23 // LWG3813: span<volatile class> is generally unsupported since C++23.
+  check_non_volatile<std::string>();
+  check_non_volatile<A>();
+#else
   check_cvs<std::string>();
   check_cvs<A>();
+#endif
   return true;
 }
 
diff --git a/libcxx/test/std/containers/views/views.span/types.pass.cpp b/libcxx/test/std/containers/views/views.span/types.pass.cpp
index be17884dae2955..f3e0f64c93e94e 100644
--- a/libcxx/test/std/containers/views/views.span/types.pass.cpp
+++ b/libcxx/test/std/containers/views/views.span/types.pass.cpp
@@ -66,30 +66,39 @@ void testSpan()
     testIterator<S, typename S::reverse_iterator>();
 }
 
+template <typename T>
+void test_non_volatile() {
+  testSpan<std::span<T>, T, std::dynamic_extent>();
+  testSpan<std::span<const T>, const T, std::dynamic_extent>();
+
+  testSpan<std::span<T, 5>, T, 5>();
+  testSpan<std::span<const T, 5>, const T, 5>();
+}
 
 template <typename T>
-void test()
-{
-    testSpan<std::span<               T>,                T, std::dynamic_extent>();
-    testSpan<std::span<const          T>, const          T, std::dynamic_extent>();
-    testSpan<std::span<      volatile T>,       volatile T, std::dynamic_extent>();
-    testSpan<std::span<const volatile T>, const volatile T, std::dynamic_extent>();
-
-    testSpan<std::span<               T, 5>,                T, 5>();
-    testSpan<std::span<const          T, 5>, const          T, 5>();
-    testSpan<std::span<      volatile T, 5>,       volatile T, 5>();
-    testSpan<std::span<const volatile T, 5>, const volatile T, 5>();
+void test() {
+  test_non_volatile<T>();
+
+  testSpan<std::span<volatile T>, volatile T, std::dynamic_extent>();
+  testSpan<std::span<const volatile T>, const volatile T, std::dynamic_extent>();
+
+  testSpan<std::span<volatile T, 5>, volatile T, 5>();
+  testSpan<std::span<const volatile T, 5>, const volatile T, 5>();
 }
 
-struct A{};
+struct A {};
 
-int main(int, char**)
-{
-    test<int>();
-    test<long>();
-    test<double>();
-    test<std::string>();
-    test<A>();
+int main(int, char**) {
+  test<int>();
+  test<long>();
+  test<double>();
+#if TEST_STD_VER >= 23 // LWG3813: span<volatile class> is generally unsupported since C++23.
+  test_non_volatile<std::string>();
+  test_non_volatile<A>();
+#else
+  test<std::string>();
+  test<A>();
+#endif
 
   return 0;
 }

>From 40e7a25bb38954779756bdde3dd26f8c7085a1d3 Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Fri, 1 Nov 2024 14:10:27 +0800
Subject: [PATCH 4/5] Transitive inclusions and forward declaration of `span`

---
 libcxx/include/__ranges/as_const_view.h | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__ranges/as_const_view.h b/libcxx/include/__ranges/as_const_view.h
index d991f4641e532d..3c520447b7224c 100644
--- a/libcxx/include/__ranges/as_const_view.h
+++ b/libcxx/include/__ranges/as_const_view.h
@@ -11,6 +11,8 @@
 #define _LIBCPP___RANGES_AS_CONST_VIEW_H
 
 #include <__concepts/constructible.h>
+#include <__cstddef/size_t.h>
+#include <__fwd/span.h>
 #include <__iterator/concepts.h>
 #include <__ranges/all.h>
 #include <__ranges/concepts.h>
@@ -22,13 +24,12 @@
 #include <__ranges/view_interface.h>
 #include <__type_traits/is_reference.h>
 #include <__type_traits/is_specialization.h>
+#include <__type_traits/remove_cvref.h>
 #include <__utility/auto_cast.h>
 #include <__utility/declval.h>
 #include <__utility/forward.h>
 #include <__utility/move.h>
 #include <__utility/pair.h>
-#include <cstddef>
-#include <span>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header

>From 8b50c880419e2b7f259326e5df9303d3f03b867f Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Fri, 1 Nov 2024 14:18:11 +0800
Subject: [PATCH 5/5] Missing inclusion of `<__ranges/enable_borrowed_range.h>`

---
 libcxx/include/__ranges/as_const_view.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libcxx/include/__ranges/as_const_view.h b/libcxx/include/__ranges/as_const_view.h
index 3c520447b7224c..a750c9f36f134a 100644
--- a/libcxx/include/__ranges/as_const_view.h
+++ b/libcxx/include/__ranges/as_const_view.h
@@ -18,6 +18,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/const_access.h>
 #include <__ranges/empty_view.h>
+#include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
 #include <__ranges/ref_view.h>
 #include <__ranges/size.h>



More information about the libcxx-commits mailing list