[flang-commits] [lld] [clang] [clang-tools-extra] [libcxx] [compiler-rt] [flang] [llvm] [libc++][ranges] P2116R9: Implements `views::enumerate` (PR #73617)

Hristo Hristov via flang-commits flang-commits at lists.llvm.org
Mon Jan 1 06:01:50 PST 2024


https://github.com/H-G-Hristov updated https://github.com/llvm/llvm-project/pull/73617

>From 75fe088c9f38887553bd359353b11a863ca6f7e6 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <zingam at outlook.com>
Date: Thu, 15 Jun 2023 01:59:18 +0300
Subject: [PATCH 1/5] [libc++][ranges] P2116R9: Implements `views::enumerate`

---
 libcxx/docs/FeatureTestMacroTable.rst         |   2 +
 libcxx/docs/Status/Cxx23Papers.csv            |   2 +-
 libcxx/docs/Status/Cxx2cIssues.csv            |   4 +-
 libcxx/docs/Status/RangesViews.csv            |   2 +-
 libcxx/include/CMakeLists.txt                 |   1 +
 libcxx/include/__ranges/enumerate_view.h      | 333 ++++++++++++++++++
 libcxx/include/module.modulemap.in            |   1 +
 libcxx/include/ranges                         |  12 +
 libcxx/include/version                        |   2 +
 libcxx/modules/std/ranges.inc                 |  13 +-
 .../ranges.version.compile.pass.cpp           |  31 ++
 .../version.version.compile.pass.cpp          |  31 ++
 ...erator_robust_against_adl.compile.pass.cpp |   1 +
 .../range.enumerate/adaptor.pass.cpp          | 128 +++++++
 .../range.enumerate/base.pass.cpp             |  85 +++++
 .../range.enumerate/begin.pass.cpp            | 129 +++++++
 .../range.enumerate/ctad.compile.pass.cpp     |  46 +++
 .../range.enumerate/ctor.base.pass.cpp        |  55 +++
 .../range.enumerate/ctor.default.pass.cpp     |  81 +++++
 .../enable_borrowed_range.compile.pass.cpp    |  35 ++
 .../range.enumerate/end.pass.cpp              | 114 ++++++
 .../iterator/arithmetic.pass.cpp              | 127 +++++++
 .../range.enumerate/iterator/base.pass.cpp    |  81 +++++
 .../iterator/compare.three_way.pass.cpp       | 107 ++++++
 .../iterator/ctor.convert.pass.cpp            |  84 +++++
 .../iterator/ctor.default.pass.cpp            |  69 ++++
 .../range.enumerate/iterator/deref.pass.cpp   | 101 ++++++
 .../range.enumerate/iterator/equal.pass.cpp   |  97 +++++
 .../range.enumerate/iterator/index.pass.cpp   |  95 +++++
 .../iterator/iter_move.pass.cpp               |  75 ++++
 .../iterator/subscript.pass.cpp               |  77 ++++
 .../iterator/types.compile.pass.cpp           | 112 ++++++
 .../range.enumerate/sentinel/base.pass.cpp    |  62 ++++
 .../sentinel/ctor.convert.pass.cpp            |  75 ++++
 .../sentinel/ctor.default.pass.cpp            |  76 ++++
 .../range.enumerate/sentinel/equal.pass.cpp   |  71 ++++
 .../range.enumerate/sentinel/minus.pass.cpp   | 253 +++++++++++++
 .../range.enumerate/size.pass.cpp             |  54 +++
 .../range.adaptors/range.enumerate/types.h    | 136 +++++++
 .../range.enumerate/types_iterators.h         | 109 ++++++
 .../generate_feature_test_macro_components.py |   5 +
 41 files changed, 2967 insertions(+), 7 deletions(-)
 create mode 100644 libcxx/include/__ranges/enumerate_view.h
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.enumerate/adaptor.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.enumerate/base.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.enumerate/begin.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.enumerate/ctad.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.enumerate/ctor.base.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.enumerate/ctor.default.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.enumerate/enable_borrowed_range.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.enumerate/end.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/arithmetic.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/base.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/compare.three_way.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/ctor.convert.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/ctor.default.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/deref.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/equal.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/index.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/iter_move.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/subscript.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/types.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.enumerate/sentinel/base.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.enumerate/sentinel/ctor.convert.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.enumerate/sentinel/ctor.default.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.enumerate/sentinel/equal.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.enumerate/sentinel/minus.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.enumerate/size.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.enumerate/types.h
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.enumerate/types_iterators.h

diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 3b2dff3108b0f6..cd1190e25bfe17 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -354,6 +354,8 @@ Status
     --------------------------------------------------- -----------------
     ``__cpp_lib_ranges_chunk_by``                       ``202202L``
     --------------------------------------------------- -----------------
+    ``__cpp_lib_ranges_enumerate``                      ``202302L``
+    --------------------------------------------------- -----------------
     ``__cpp_lib_ranges_iota``                           *unimplemented*
     --------------------------------------------------- -----------------
     ``__cpp_lib_ranges_join_with``                      *unimplemented*
diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index 5cc9e488297b9f..5083a26e4d21b9 100644
--- a/libcxx/docs/Status/Cxx23Papers.csv
+++ b/libcxx/docs/Status/Cxx23Papers.csv
@@ -105,7 +105,7 @@
 "","","","","","",""
 "`P0290R4 <https://wg21.link/P0290R4>`__","LWG", "``apply()`` for ``synchronized_value<T>``","February 2023","","","|concurrency TS|"
 "`P2770R0 <https://wg21.link/P2770R0>`__","LWG", "Stashing stashing ``iterators`` for proper flattening","February 2023","|In Progress|","","|ranges|"
-"`P2164R9 <https://wg21.link/P2164R9>`__","LWG", "``views::enumerate``","February 2023","","","|ranges|"
+"`P2164R9 <https://wg21.link/P2164R9>`__","LWG", "``views::enumerate``","February 2023","|Complete|","18.0","|ranges|"
 "`P2711R1 <https://wg21.link/P2711R1>`__","LWG", "Making multi-param constructors of ``views`` ``explicit``","February 2023","|In Progress| [#note-P2711R1]_","","|ranges|"
 "`P2609R3 <https://wg21.link/P2609R3>`__","LWG", "Relaxing Ranges Just A Smidge","February 2023","","","|ranges|"
 "`P2713R1 <https://wg21.link/P2713R1>`__","LWG", "Escaping improvements in ``std::format``","February 2023","","","|format|"
diff --git a/libcxx/docs/Status/Cxx2cIssues.csv b/libcxx/docs/Status/Cxx2cIssues.csv
index fe0f13f6e8cb2c..d12dc23cb4a8ca 100644
--- a/libcxx/docs/Status/Cxx2cIssues.csv
+++ b/libcxx/docs/Status/Cxx2cIssues.csv
@@ -8,8 +8,8 @@
 "`3903 <https://wg21.link/LWG3903>`__","span destructor is redundantly noexcept","Varna June 2023","|Complete|","7.0",""
 "`3904 <https://wg21.link/LWG3904>`__","``lazy_split_view::outer-iterator``'s const-converting constructor isn't setting ``trailing_empty_``","Varna June 2023","","","|ranges|"
 "`3905 <https://wg21.link/LWG3905>`__","Type of ``std::fexcept_t``","Varna June 2023","|Complete|","3.4",""
-"`3912 <https://wg21.link/LWG3912>`__","``enumerate_view::iterator::operator-`` should be ``noexcept``","Varna June 2023","","","|ranges|"
-"`3914 <https://wg21.link/LWG3914>`__","Inconsistent template-head of ``ranges::enumerate_view``","Varna June 2023","","","|ranges|"
+"`3912 <https://wg21.link/LWG3912>`__","``enumerate_view::iterator::operator-`` should be ``noexcept``","Varna June 2023","|Complete|","18.0","|ranges|"
+"`3914 <https://wg21.link/LWG3914>`__","Inconsistent template-head of ``ranges::enumerate_view``","Varna June 2023","|Complete|","18.0","|ranges|"
 "`3915 <https://wg21.link/LWG3915>`__","Redundant paragraph about expression variations","Varna June 2023","","","|ranges|"
 "`3925 <https://wg21.link/LWG3925>`__","Concept ``formattable``'s definition is incorrect","Varna June 2023","|Complete|","17.0","|format|"
 "`3927 <https://wg21.link/LWG3927>`__","Unclear preconditions for ``operator[]`` for sequence containers","Varna June 2023","|Nothing To Do|","",""
diff --git a/libcxx/docs/Status/RangesViews.csv b/libcxx/docs/Status/RangesViews.csv
index f141656eb131a2..581596724878b2 100644
--- a/libcxx/docs/Status/RangesViews.csv
+++ b/libcxx/docs/Status/RangesViews.csv
@@ -35,4 +35,4 @@ C++23,`chunk_by <https://wg21.link/P2443R1>`_,Jakub Mazurkiewicz,`D144767 <https
 C++23,`as_const <https://wg21.link/P2278R4>`_,Unassigned,No patch yet,Not started
 C++23,`as_rvalue <https://wg21.link/P2446R2>`_,Nikolas Klauser,`D137637 <https://llvm.org/D137637>`_,✅
 C++23,`stride <https://wg21.link/P1899R3>`_,Hristo Hristov and Will Hawkins,`D156924 <https://llvm.org/D156924>`_,In Progress
-C++23,`enumerate <https://wg21.link/P2164R9>`_,Hristo Hristov,`D157193 <https://reviews.llvm.org/D157193>`_,In Progress
+C++23,`enumerate <https://wg21.link/P2164R9>`_,Hristo Hristov,`D157193 <https://reviews.llvm.org/D157193>`_,✅
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index b0bc3718576f66..eca80630bb1c1e 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -622,6 +622,7 @@ set(files
   __ranges/empty_view.h
   __ranges/enable_borrowed_range.h
   __ranges/enable_view.h
+  __ranges/enumerate_view.h
   __ranges/filter_view.h
   __ranges/from_range.h
   __ranges/iota_view.h
diff --git a/libcxx/include/__ranges/enumerate_view.h b/libcxx/include/__ranges/enumerate_view.h
new file mode 100644
index 00000000000000..77b29837ba5460
--- /dev/null
+++ b/libcxx/include/__ranges/enumerate_view.h
@@ -0,0 +1,333 @@
+// -*- 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_ENUMERATE_VIEW_H
+#define _LIBCPP___RANGES_ENUMERATE_VIEW_H
+
+#include <__concepts/convertible_to.h>
+#include <__config>
+#include <__iterator/concepts.h>
+#include <__iterator/distance.h>
+#include <__iterator/iter_move.h>
+#include <__iterator/iterator_traits.h>
+#include <__ranges/access.h>
+#include <__ranges/all.h>
+#include <__ranges/concepts.h>
+#include <__ranges/enable_borrowed_range.h>
+#include <__ranges/range_adaptor.h>
+#include <__ranges/size.h>
+#include <__ranges/view_interface.h>
+#include <__type_traits/maybe_const.h>
+#include <__utility/forward.h>
+#include <__utility/move.h>
+#include <tuple>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 23
+
+namespace ranges {
+
+// [concept.object]
+
+template <class _Rp>
+concept __range_with_movable_references =
+    ranges::input_range<_Rp> && std::move_constructible<ranges::range_reference_t<_Rp>> &&
+    std::move_constructible<ranges::range_rvalue_reference_t<_Rp>>;
+
+// [range.enumerate.view]
+
+template <view _View>
+  requires __range_with_movable_references<_View>
+class enumerate_view : public view_interface<enumerate_view<_View>> {
+  _View __base_ = _View();
+
+  // [range.enumerate.iterator]
+  template <bool _Const>
+  class __iterator;
+
+  // [range.enumerate.sentinel]
+  template <bool _Const>
+  class __sentinel;
+
+  template <bool _AnyConst>
+  _LIBCPP_HIDE_FROM_ABI static constexpr decltype(auto) __get_current(const __iterator<_AnyConst>& __iter) {
+    return (__iter.__current_);
+  }
+
+public:
+  _LIBCPP_HIDE_FROM_ABI constexpr enumerate_view()
+    requires(default_initializable<_View>)
+  = default;
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit enumerate_view(_View __base) : __base_(std::move(__base)){};
+
+  _LIBCPP_HIDE_FROM_ABI constexpr auto begin()
+    requires(!__simple_view<_View>)
+  {
+    return __iterator<false>(ranges::begin(__base_), 0);
+  }
+  _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
+    requires __range_with_movable_references<const _View>
+  {
+    return __iterator<true>(ranges::begin(__base_), 0);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr auto end()
+    requires(!__simple_view<_View>)
+  {
+    if constexpr (common_range<_View> && sized_range<_View>)
+      return __iterator<false>(ranges::end(__base_), ranges::distance(__base_));
+    else
+      return __sentinel<false>(ranges::end(__base_));
+  }
+  _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
+    requires __range_with_movable_references<const _View>
+  {
+    if constexpr (common_range<const _View> && sized_range<const _View>)
+      return __iterator<true>(ranges::end(__base_), ranges::distance(__base_));
+    else
+      return __sentinel<true>(ranges::end(__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_);
+  }
+
+  _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_); }
+};
+
+template <class _Range>
+enumerate_view(_Range&&) -> enumerate_view<views::all_t<_Range>>;
+
+// [range.enumerate.iterator]
+
+template <view _View>
+  requires __range_with_movable_references<_View>
+template <bool _Const>
+class enumerate_view<_View>::__iterator {
+  using _Base = __maybe_const<_Const, _View>;
+
+  static consteval auto __get_iterator_concept() {
+    if constexpr (random_access_range<_Base>) {
+      return random_access_iterator_tag{};
+    } else if constexpr (bidirectional_range<_Base>) {
+      return bidirectional_iterator_tag{};
+    } else if constexpr (forward_range<_Base>) {
+      return forward_iterator_tag{};
+    } else {
+      return input_iterator_tag{};
+    }
+  }
+
+  friend class enumerate_view<_View>;
+
+public:
+  using iterator_category = input_iterator_tag;
+  using iterator_concept  = decltype(__get_iterator_concept());
+  using difference_type   = range_difference_t<_Base>;
+  using value_type        = tuple<difference_type, range_value_t<_Base>>;
+
+private:
+  using __reference_type       = tuple<difference_type, range_reference_t<_Base>>;
+  iterator_t<_Base> __current_ = iterator_t<_Base>();
+  difference_type __pos_       = 0;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(iterator_t<_Base> __current, difference_type __pos)
+      : __current_(std::move(__current)), __pos_(__pos) {}
+
+public:
+  _LIBCPP_HIDE_FROM_ABI __iterator()
+    requires(default_initializable<iterator_t<_Base>>)
+  = default;
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(__iterator<!_Const> __i)
+    requires _Const && convertible_to<iterator_t<_View>, iterator_t<_Base>>
+      : __current_(std::move(__i.__current_)), __pos_(__i.__pos_) {}
+
+  _LIBCPP_HIDE_FROM_ABI constexpr const iterator_t<_Base>& base() const& noexcept { return __current_; }
+  _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_Base> base() && { return std::move(__current_); }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr difference_type index() const noexcept { return __pos_; }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr auto operator*() const { return __reference_type(__pos_, *__current_); }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator++() {
+    ++__current_;
+    ++__pos_;
+    return *this;
+  }
+  _LIBCPP_HIDE_FROM_ABI constexpr void operator++(int) { return ++*this; }
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator++(int)
+    requires forward_range<_Base>
+  {
+    auto __temp = *this;
+    ++*this;
+    return __temp;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator--()
+    requires bidirectional_range<_Base>
+  {
+    --__current_;
+    --__pos_;
+    return *this;
+  }
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator--(int)
+    requires bidirectional_range<_Base>
+  {
+    auto __temp = *this;
+    --*this;
+    return *__temp;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator+=(difference_type __n)
+    requires random_access_range<_Base>
+  {
+    __current_ += __n;
+    __pos_ += __n;
+    return *this;
+  }
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator-=(difference_type __n)
+    requires random_access_range<_Base>
+  {
+    __current_ -= __n;
+    __pos_ -= __n;
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr auto operator[](difference_type __n) const
+    requires random_access_range<_Base>
+  {
+    return __reference_type(__pos_ + __n, __current_[__n]);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __x, const __iterator& __y) noexcept {
+    return __x.__pos_ == __y.__pos_;
+  }
+  _LIBCPP_HIDE_FROM_ABI friend constexpr strong_ordering
+  operator<=>(const __iterator& __x, const __iterator& __y) noexcept {
+    return __x.__pos_ <=> __y.__pos_;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(const __iterator& __i, difference_type __n)
+    requires random_access_range<_Base>
+  {
+    auto __temp = __i;
+    __temp += __n;
+    return __temp;
+  }
+  _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(difference_type __n, const __iterator& __i)
+    requires random_access_range<_Base>
+  {
+    return __i + __n;
+  }
+  _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator-(const __iterator& __i, difference_type __n)
+    requires random_access_range<_Base>
+  {
+    auto __temp = __i;
+    __temp -= __n;
+    return __temp;
+  }
+  _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(const __iterator& __x, const __iterator& __y) noexcept {
+    return __x.__pos_ - __y.__pos_;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI friend constexpr auto iter_move(const __iterator& __i) noexcept(
+      noexcept(ranges::iter_move(__i.__current_)) && is_nothrow_move_constructible_v<range_rvalue_reference_t<_Base>>) {
+    return tuple<difference_type, range_rvalue_reference_t<_Base>>(__i.__pos_, ranges::iter_move(__i.__current_));
+  }
+};
+
+// [range.enumerate.sentinel]
+
+template <view _View>
+  requires __range_with_movable_references<_View>
+template <bool _Const>
+class enumerate_view<_View>::__sentinel {
+  using _Base              = __maybe_const<_Const, _View>;
+  sentinel_t<_Base> __end_ = sentinel_t<_Base>();
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(std::move(__end)) {}
+
+  friend class enumerate_view<_View>;
+
+public:
+  _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
+  _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel<!_Const> __other)
+    requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
+      : __end_(std::move(__other.__end_)) {}
+
+  _LIBCPP_HIDE_FROM_ABI constexpr sentinel_t<_Base> base() const { return __end_; }
+
+  template <bool _OtherConst>
+    requires sentinel_for<sentinel_t<_Base>, iterator_t<__maybe_const<_OtherConst, _View>>>
+  _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator<_OtherConst>& __x, const __sentinel& __y) {
+    return __get_current(__x) == __y.__end_;
+  }
+
+  template <bool _OtherConst>
+    requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<__maybe_const<_OtherConst, _View>>>
+  _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _View>>
+  operator-(const __iterator<_OtherConst>& __x, const __sentinel& __y) {
+    return __get_current(__x) - __y.__end_;
+  }
+
+  template <bool _OtherConst>
+    requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<__maybe_const<_OtherConst, _View>>>
+  _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _View>>
+  operator-(const __sentinel& __x, const __iterator<_OtherConst>& __y) {
+    return __x.__end_ - __get_current(__y);
+  }
+};
+
+template <class _View>
+constexpr bool enable_borrowed_range<enumerate_view<_View>> = enable_borrowed_range<_View>;
+
+namespace views {
+namespace __enumerate {
+
+struct __fn : __range_adaptor_closure<__fn> {
+  template <class _Range>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Range&& __range) const
+      noexcept(noexcept(/**/ enumerate_view(std::forward<_Range>(__range))))
+          -> decltype(/*--*/ enumerate_view(std::forward<_Range>(__range))) {
+    return /*-------------*/ enumerate_view(std::forward<_Range>(__range));
+  }
+};
+
+} // namespace __enumerate
+
+inline namespace __cpo {
+
+inline constexpr auto enumerate = __enumerate::__fn{};
+
+} // namespace __cpo
+} // namespace views
+} // namespace ranges
+
+#endif // _LIBCPP_STD_VER >= 23
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___RANGES_ENUMERATE_VIEW_H
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 90381f4f7f720d..c0949a28b278b5 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1663,6 +1663,7 @@ module std_private_ranges_empty                      [system] { header "__ranges
 module std_private_ranges_empty_view                 [system] { header "__ranges/empty_view.h" }
 module std_private_ranges_enable_borrowed_range      [system] { header "__ranges/enable_borrowed_range.h" }
 module std_private_ranges_enable_view                [system] { header "__ranges/enable_view.h" }
+module std_private_ranges_enumerate_view             [system] { header "__ranges/enumerate_view.h" }
 module std_private_ranges_filter_view                [system] {
   header "__ranges/filter_view.h"
   export std_private_ranges_range_adaptor
diff --git a/libcxx/include/ranges b/libcxx/include/ranges
index f71a92f8a660b0..56b3c82daa1181 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -314,6 +314,17 @@ namespace std::ranges {
 
   namespace views { template<class T> inline constexpr unspecified istream = unspecified; }
 
+  // [range.enumerate], enumerate view
+  template<view View>
+    requires see below
+  class enumerate_view;                                                             // since C++23
+
+  template<class View>
+    constexpr bool enable_borrowed_range<enumerate_view<View>> =                    // since C++23
+      enable_borrowed_range<View>;
+
+  namespace views { inline constexpr unspecified enumerate = unspecified; }         // since C++23
+
   // [range.zip], zip view
   template<input_range... Views>
     requires (view<Views> && ...) && (sizeof...(Views) > 0)
@@ -393,6 +404,7 @@ namespace std {
 #include <__ranges/empty_view.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/enable_view.h>
+#include <__ranges/enumerate_view.h>
 #include <__ranges/filter_view.h>
 #include <__ranges/from_range.h>
 #include <__ranges/iota_view.h>
diff --git a/libcxx/include/version b/libcxx/include/version
index ef01af75329822..ed6c2aa9fcd42f 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -170,6 +170,7 @@ __cpp_lib_ranges_as_const                               202207L <ranges>
 __cpp_lib_ranges_as_rvalue                              202207L <ranges>
 __cpp_lib_ranges_chunk                                  202202L <ranges>
 __cpp_lib_ranges_chunk_by                               202202L <ranges>
+__cpp_lib_ranges_enumerate                              202302L <ranges>
 __cpp_lib_ranges_iota                                   202202L <numeric>
 __cpp_lib_ranges_join_with                              202202L <ranges>
 __cpp_lib_ranges_repeat                                 202207L <ranges>
@@ -461,6 +462,7 @@ __cpp_lib_within_lifetime                               202306L <type_traits>
 # define __cpp_lib_ranges_as_rvalue                     202207L
 // # define __cpp_lib_ranges_chunk                         202202L
 # define __cpp_lib_ranges_chunk_by                      202202L
+# define __cpp_lib_ranges_enumerate                     202302L
 // # define __cpp_lib_ranges_iota                          202202L
 // # define __cpp_lib_ranges_join_with                     202202L
 # define __cpp_lib_ranges_repeat                        202207L
diff --git a/libcxx/modules/std/ranges.inc b/libcxx/modules/std/ranges.inc
index a883103d812588..5b97f02c56a495 100644
--- a/libcxx/modules/std/ranges.inc
+++ b/libcxx/modules/std/ranges.inc
@@ -320,9 +320,8 @@ export namespace std {
     namespace views {
       using std::ranges::views::chunk_by;
     }
-#endif // _LIBCPP_STD_VER >= 23
 
-#if 0
+#  if 0
     // [range.stride], stride view
     using std::ranges::stride_view;
 
@@ -335,7 +334,15 @@ export namespace std {
     namespace views {
       using std::ranges::views::cartesian_product;
     }
-#endif
+#  endif
+
+    // [range.enumerate]
+    using std::ranges::enumerate_view;
+
+    namespace views {
+      using std::ranges::views::enumerate;
+    }
+#endif // _LIBCPP_STD_VER >= 23
   } // namespace ranges
 
   namespace views = ranges::views;
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 aa3a4964ad492e..0d5bf118892a42 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
@@ -21,6 +21,7 @@
     __cpp_lib_ranges_as_rvalue       202207L [C++23]
     __cpp_lib_ranges_chunk           202202L [C++23]
     __cpp_lib_ranges_chunk_by        202202L [C++23]
+    __cpp_lib_ranges_enumerate       202302L [C++23]
     __cpp_lib_ranges_join_with       202202L [C++23]
     __cpp_lib_ranges_repeat          202207L [C++23]
     __cpp_lib_ranges_slide           202202L [C++23]
@@ -53,6 +54,10 @@
 #   error "__cpp_lib_ranges_chunk_by should not be defined before c++23"
 # endif
 
+# ifdef __cpp_lib_ranges_enumerate
+#   error "__cpp_lib_ranges_enumerate should not be defined before c++23"
+# endif
+
 # ifdef __cpp_lib_ranges_join_with
 #   error "__cpp_lib_ranges_join_with should not be defined before c++23"
 # endif
@@ -95,6 +100,10 @@
 #   error "__cpp_lib_ranges_chunk_by should not be defined before c++23"
 # endif
 
+# ifdef __cpp_lib_ranges_enumerate
+#   error "__cpp_lib_ranges_enumerate should not be defined before c++23"
+# endif
+
 # ifdef __cpp_lib_ranges_join_with
 #   error "__cpp_lib_ranges_join_with should not be defined before c++23"
 # endif
@@ -137,6 +146,10 @@
 #   error "__cpp_lib_ranges_chunk_by should not be defined before c++23"
 # endif
 
+# ifdef __cpp_lib_ranges_enumerate
+#   error "__cpp_lib_ranges_enumerate should not be defined before c++23"
+# endif
+
 # ifdef __cpp_lib_ranges_join_with
 #   error "__cpp_lib_ranges_join_with should not be defined before c++23"
 # endif
@@ -182,6 +195,10 @@
 #   error "__cpp_lib_ranges_chunk_by should not be defined before c++23"
 # endif
 
+# ifdef __cpp_lib_ranges_enumerate
+#   error "__cpp_lib_ranges_enumerate should not be defined before c++23"
+# endif
+
 # ifdef __cpp_lib_ranges_join_with
 #   error "__cpp_lib_ranges_join_with should not be defined before c++23"
 # endif
@@ -251,6 +268,13 @@
 #   error "__cpp_lib_ranges_chunk_by should have the value 202202L in c++23"
 # endif
 
+# ifndef __cpp_lib_ranges_enumerate
+#   error "__cpp_lib_ranges_enumerate should be defined in c++23"
+# endif
+# if __cpp_lib_ranges_enumerate != 202302L
+#   error "__cpp_lib_ranges_enumerate should have the value 202302L in c++23"
+# endif
+
 # if !defined(_LIBCPP_VERSION)
 #   ifndef __cpp_lib_ranges_join_with
 #     error "__cpp_lib_ranges_join_with should be defined in c++23"
@@ -353,6 +377,13 @@
 #   error "__cpp_lib_ranges_chunk_by should have the value 202202L in c++26"
 # endif
 
+# ifndef __cpp_lib_ranges_enumerate
+#   error "__cpp_lib_ranges_enumerate should be defined in c++26"
+# endif
+# if __cpp_lib_ranges_enumerate != 202302L
+#   error "__cpp_lib_ranges_enumerate should have the value 202302L in c++26"
+# endif
+
 # if !defined(_LIBCPP_VERSION)
 #   ifndef __cpp_lib_ranges_join_with
 #     error "__cpp_lib_ranges_join_with should be defined in c++26"
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 d7adf2941b62c6..334541501045d9 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
@@ -158,6 +158,7 @@
     __cpp_lib_ranges_as_rvalue                       202207L [C++23]
     __cpp_lib_ranges_chunk                           202202L [C++23]
     __cpp_lib_ranges_chunk_by                        202202L [C++23]
+    __cpp_lib_ranges_enumerate                       202302L [C++23]
     __cpp_lib_ranges_iota                            202202L [C++23]
     __cpp_lib_ranges_join_with                       202202L [C++23]
     __cpp_lib_ranges_repeat                          202207L [C++23]
@@ -771,6 +772,10 @@
 #   error "__cpp_lib_ranges_chunk_by should not be defined before c++23"
 # endif
 
+# ifdef __cpp_lib_ranges_enumerate
+#   error "__cpp_lib_ranges_enumerate should not be defined before c++23"
+# endif
+
 # ifdef __cpp_lib_ranges_iota
 #   error "__cpp_lib_ranges_iota should not be defined before c++23"
 # endif
@@ -1593,6 +1598,10 @@
 #   error "__cpp_lib_ranges_chunk_by should not be defined before c++23"
 # endif
 
+# ifdef __cpp_lib_ranges_enumerate
+#   error "__cpp_lib_ranges_enumerate should not be defined before c++23"
+# endif
+
 # ifdef __cpp_lib_ranges_iota
 #   error "__cpp_lib_ranges_iota should not be defined before c++23"
 # endif
@@ -2586,6 +2595,10 @@
 #   error "__cpp_lib_ranges_chunk_by should not be defined before c++23"
 # endif
 
+# ifdef __cpp_lib_ranges_enumerate
+#   error "__cpp_lib_ranges_enumerate should not be defined before c++23"
+# endif
+
 # ifdef __cpp_lib_ranges_iota
 #   error "__cpp_lib_ranges_iota should not be defined before c++23"
 # endif
@@ -3858,6 +3871,10 @@
 #   error "__cpp_lib_ranges_chunk_by should not be defined before c++23"
 # endif
 
+# ifdef __cpp_lib_ranges_enumerate
+#   error "__cpp_lib_ranges_enumerate should not be defined before c++23"
+# endif
+
 # ifdef __cpp_lib_ranges_iota
 #   error "__cpp_lib_ranges_iota should not be defined before c++23"
 # endif
@@ -5328,6 +5345,13 @@
 #   error "__cpp_lib_ranges_chunk_by should have the value 202202L in c++23"
 # endif
 
+# ifndef __cpp_lib_ranges_enumerate
+#   error "__cpp_lib_ranges_enumerate should be defined in c++23"
+# endif
+# if __cpp_lib_ranges_enumerate != 202302L
+#   error "__cpp_lib_ranges_enumerate should have the value 202302L in c++23"
+# endif
+
 # if !defined(_LIBCPP_VERSION)
 #   ifndef __cpp_lib_ranges_iota
 #     error "__cpp_lib_ranges_iota should be defined in c++23"
@@ -7047,6 +7071,13 @@
 #   error "__cpp_lib_ranges_chunk_by should have the value 202202L in c++26"
 # endif
 
+# ifndef __cpp_lib_ranges_enumerate
+#   error "__cpp_lib_ranges_enumerate should be defined in c++26"
+# endif
+# if __cpp_lib_ranges_enumerate != 202302L
+#   error "__cpp_lib_ranges_enumerate should have the value 202302L in c++26"
+# endif
+
 # if !defined(_LIBCPP_VERSION)
 #   ifndef __cpp_lib_ranges_iota
 #     error "__cpp_lib_ranges_iota should be defined in c++26"
diff --git a/libcxx/test/std/ranges/iterator_robust_against_adl.compile.pass.cpp b/libcxx/test/std/ranges/iterator_robust_against_adl.compile.pass.cpp
index 09b77c0901a229..2dfd4efaded35e 100644
--- a/libcxx/test/std/ranges/iterator_robust_against_adl.compile.pass.cpp
+++ b/libcxx/test/std/ranges/iterator_robust_against_adl.compile.pass.cpp
@@ -76,4 +76,5 @@ static_assert(!CanFindADLFunc<std::ranges::transform_view<adl::BaseView, adl::Pr
 
 #if TEST_STD_VER >= 23
 static_assert(!CanFindADLFunc<std::ranges::zip_view<adl::BaseView>>);
+static_assert(!CanFindADLFunc<std::ranges::enumerate_view<adl::BaseView>>);
 #endif
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/adaptor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/adaptor.pass.cpp
new file mode 100644
index 00000000000000..edc58536d68dcd
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/adaptor.pass.cpp
@@ -0,0 +1,128 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// class enumerate_view
+
+// std::views::enumerate;
+
+#include <algorithm>
+#include <cassert>
+#include <ranges>
+#include <string_view>
+
+#include "types.h"
+
+// Concepts
+
+template <class View, class T>
+concept CanBePiped = requires(View&& view, T&& t) {
+  { std::forward<View>(view) | std::forward<T>(t) };
+};
+
+template <class Range>
+concept CanEnumerate = requires(Range&& range) { std::views::enumerate(std::forward<Range>(range)); };
+
+// Helpers
+
+struct ImmovableReference {
+  ImmovableReference(ImmovableReference&&) = delete;
+
+  operator int();
+};
+
+struct IteratorWithImmovableReferences {
+  using value_type      = int;
+  using difference_type = std::ptrdiff_t;
+
+  ImmovableReference operator*() const;
+  IteratorWithImmovableReferences& operator++();
+  void operator++(int);
+  bool operator==(std::default_sentinel_t) const;
+};
+
+static_assert(std::input_iterator<IteratorWithImmovableReferences>);
+
+using NonEnumeratableRangeWithImmmovabaleReferences =
+    std::ranges::subrange<IteratorWithImmovableReferences, std::default_sentinel_t>;
+
+static_assert(std::ranges::input_range<NonEnumeratableRangeWithImmmovabaleReferences>);
+static_assert(!CanEnumerate<NonEnumeratableRangeWithImmmovabaleReferences>);
+
+template <typename View, typename T>
+using ExpectedViewElement = std::tuple<typename std::ranges::iterator_t<View>::difference_type, T>;
+
+// Helpers
+
+template <typename View, typename T = int>
+constexpr void compareViews(View v, std::initializer_list<ExpectedViewElement<View, T>> list) {
+  assert(std::ranges::equal(v, list));
+}
+
+// Test SFINAE friendliness
+
+static_assert(CanBePiped<RangeView, decltype(std::views::enumerate)>);
+
+static_assert(CanEnumerate<RangeView>);
+
+static_assert(!std::is_invocable_v<decltype(std::views::enumerate)>);
+static_assert(std::is_invocable_v<decltype(std::views::enumerate), RangeView>);
+static_assert(!std::is_invocable_v<decltype(std::views::enumerate), NotAView>);
+static_assert(!std::is_invocable_v<decltype(std::views::enumerate), NotInvocable>);
+
+static_assert(std::is_same_v<decltype(std::ranges::views::enumerate), decltype(std::views::enumerate)>);
+
+constexpr bool test() {
+  // Test `views::enumerate_view(v)`
+  {
+    int buff[] = {0, 1, 2, 3};
+
+    using Result = std::ranges::enumerate_view<RangeView>;
+    RangeView const range(buff, buff + 4);
+
+    std::same_as<Result> decltype(auto) result = std::views::enumerate(range);
+    compareViews(result, {{0, 0}, {1, 1}, {2, 2}, {3, 3}});
+  }
+  {
+    std::string_view sv{"babazmt"};
+    using Result = std::ranges::enumerate_view<std::string_view>;
+
+    std::same_as<Result> decltype(auto) result = std::views::enumerate(sv);
+    compareViews(result, {{0, 'b'}, {1, 'a'}, {2, 'b'}, {3, 'a'}, {4, 'z'}, {5, 'm'}, {6, 't'}});
+  }
+
+  // Test `adaptor | views::enumerate`
+  {
+    int buff[] = {0, 1, 2, 3};
+
+    using Result = std::ranges::enumerate_view<RangeView>;
+    RangeView const range(buff, buff + 4);
+
+    std::same_as<Result> decltype(auto) result = range | std::views::enumerate;
+    compareViews(result, {{0, 0}, {1, 1}, {2, 2}, {3, 3}});
+  }
+  {
+    std::string_view sv{"babazmt"};
+    using Result = std::ranges::enumerate_view<std::string_view>;
+
+    std::same_as<Result> decltype(auto) result = sv | std::views::enumerate;
+    compareViews(result, {{0, 'b'}, {1, 'a'}, {2, 'b'}, {3, 'a'}, {4, 'z'}, {5, 'm'}, {6, 't'}});
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/base.pass.cpp
new file mode 100644
index 00000000000000..86d8a9e88cb99c
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/base.pass.cpp
@@ -0,0 +1,85 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// class enumerate_view
+
+// constexpr V base() const & requires copy_constructible<V>;
+// constexpr V base() &&;
+
+#include <cassert>
+#include <ranges>
+
+#include "MoveOnly.h"
+#include "types.h"
+
+template <class T>
+concept HasBase = requires(T&& t) { std::forward<T>(t).base(); };
+
+// SFINAE
+
+static_assert(HasBase<std::ranges::enumerate_view<RangeView> const&>);
+static_assert(HasBase<std::ranges::enumerate_view<RangeView>&&>);
+
+struct MoveOnlyView : RangeView {
+  MoveOnly mo;
+};
+
+static_assert(!HasBase<std::ranges::enumerate_view<MoveOnlyView> const&>);
+static_assert(HasBase<std::ranges::enumerate_view<MoveOnlyView>&&>);
+
+constexpr bool test() {
+  // Check the const& overload
+  {
+    int buff[] = {0, 1, 2, 3};
+
+    RangeView range(buff, buff + 4);
+
+    std::ranges::enumerate_view<RangeView> view{range};
+    std::same_as<RangeView> decltype(auto) result = view.base();
+    assert(result.wasCopyInitialized);
+    assert(range.begin() == result.begin());
+    assert(range.end() == result.end());
+  }
+  {
+    int buff[] = {0, 1, 2, 3};
+
+    RangeView const range(buff, buff + 4);
+
+    std::ranges::enumerate_view<RangeView> const view{range};
+    std::same_as<RangeView> decltype(auto) result = view.base();
+    assert(result.wasCopyInitialized);
+    assert(range.begin() == result.begin());
+    assert(range.end() == result.end());
+  }
+
+  // Check the && overload
+  {
+    int buff[] = {0, 1, 2, 3};
+
+    RangeView const range(buff, buff + 4);
+
+    std::ranges::enumerate_view<RangeView> view{range};
+    std::same_as<RangeView> decltype(auto) result = std::move(view).base();
+    assert(result.wasMoveInitialized);
+    assert(range.begin() == result.begin());
+    assert(range.end() == result.end());
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/begin.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/begin.pass.cpp
new file mode 100644
index 00000000000000..64876ed71355f1
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/begin.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
+
+// <ranges>
+
+// class enumerate_view
+
+// constexpr auto begin() requires (!simple-view<V>);
+// constexpr auto begin() const requires range-with-movable-references<const V>;
+
+#include <cassert>
+#include <concepts>
+#include <ranges>
+
+#include "test_iterators.h"
+#include "types.h"
+
+// Helpers
+
+template <class T>
+concept HasConstBegin = requires(const T ct) { ct.begin(); };
+
+template <class T>
+concept HasBegin = requires(T t) { t.begin(); };
+
+template <class T>
+concept HasConstAndNonConstBegin =
+    HasConstBegin<T> &&
+    // Because const begin() and non-const begin() returns different types: iterator<true> vs. iterator<false>
+    requires(T t, const T ct) { requires !std::same_as<decltype(t.begin()), decltype(ct.begin())>; };
+
+template <class T>
+concept HasOnlyNonConstBegin = HasBegin<T> && !HasConstBegin<T>;
+
+template <class T>
+concept HasOnlyConstBegin = HasConstBegin<T> && !HasConstAndNonConstBegin<T>;
+
+// Types
+
+template <bool Simple>
+struct CommonView : std::ranges::view_base {
+  constexpr std::tuple<std::ptrdiff_t, int>* begin()
+    requires(!Simple)
+  {
+    return nullptr;
+  }
+  constexpr const std::tuple<std::ptrdiff_t, int>* begin() const { return nullptr; }
+  constexpr std::tuple<std::ptrdiff_t, int>* end()
+    requires(!Simple)
+  {
+    return nullptr;
+  }
+  constexpr const std::tuple<std::ptrdiff_t, int>* end() const { return nullptr; }
+};
+using SimpleCommonView    = CommonView<true>;
+using NonSimpleCommonView = CommonView<false>;
+
+struct NoConstBeginView : std::ranges::view_base {
+  constexpr std::tuple<std::ptrdiff_t, int>* begin() { return nullptr; }
+  constexpr std::tuple<std::ptrdiff_t, int>* end() { return nullptr; }
+};
+
+// SFINAE
+
+// simple-view<V>
+static_assert(HasOnlyConstBegin<std::ranges::enumerate_view<SimpleCommonView>>);
+
+// !simple-view<V> && range<const V>
+static_assert(HasConstAndNonConstBegin<std::ranges::enumerate_view<NonSimpleCommonView>>);
+
+// !range<const V>
+static_assert(HasOnlyNonConstBegin<std::ranges::enumerate_view<NoConstBeginView>>);
+
+constexpr bool test() {
+  int buff[] = {1, 2, 3, 4, 5, 6, 7, 8};
+
+  // Check the return type of begin()
+  {
+    RangeView range(buff, buff + 1);
+
+    std::ranges::enumerate_view view(range);
+    using Iterator = std::ranges::iterator_t<decltype(view)>;
+    static_assert(std::same_as<Iterator, decltype(view.begin())>);
+  }
+
+  // begin() over an empty range
+  {
+    RangeView range(buff, buff);
+
+    std::ranges::enumerate_view view(range);
+    auto it = view.begin();
+    assert(base(it.base()) == buff);
+    assert(it == view.end());
+  }
+
+  // begin() over an 1-element range
+  {
+    RangeView range(buff, buff + 1);
+
+    std::ranges::enumerate_view view(range);
+    auto it = view.begin();
+    assert(base(it.base()) == buff);
+  }
+
+  // begin() over an N-element range
+  {
+    RangeView range(buff, buff + 8);
+
+    std::ranges::enumerate_view view(range);
+    auto it = view.begin();
+    assert(base(it.base()) == buff);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/ctad.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/ctad.compile.pass.cpp
new file mode 100644
index 00000000000000..fb6b26c1fd9a54
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/ctad.compile.pass.cpp
@@ -0,0 +1,46 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// class enumerate_view
+
+// template<class R>
+//   enumerate_view(R&&) -> enumerate_view<views::all_t<R>>;
+
+#include <cassert>
+#include <ranges>
+
+#include "test_iterators.h"
+#include "types.h"
+
+constexpr bool test() {
+  {
+    MinimalDefaultConstructedView jv;
+    std::ranges::enumerate_view view(jv);
+    static_assert(std::is_same_v<decltype(view), std::ranges::enumerate_view<MinimalDefaultConstructedView>>);
+  }
+
+  // Test with a range that isn't a view, to make sure we properly use views::all_t in the implementation.
+  {
+    NotAViewRange range;
+    std::ranges::enumerate_view view(range);
+    static_assert(std::is_same_v<decltype(view), std::ranges::enumerate_view<std::ranges::ref_view<NotAViewRange>>>);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/ctor.base.pass.cpp
new file mode 100644
index 00000000000000..ab33ace0cee18e
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/ctor.base.pass.cpp
@@ -0,0 +1,55 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// class enumerate_view
+
+// constexpr explicit enumerate_view(V base);
+
+#include <ranges>
+
+#include <array>
+#include <cassert>
+#include <tuple>
+#include <type_traits>
+
+#include "types.h"
+
+constexpr bool test() {
+  using EnumerateView = std::ranges::enumerate_view<RangeView>;
+
+  {
+    std::array base = {0, 1, 2, 3, 84};
+
+    RangeView range(base.begin(), base.end());
+    EnumerateView view{range};
+
+    auto baseIt = base.begin();
+    auto viewIt = view.begin();
+    for (std::size_t index = 0; index != base.size(); ++index) {
+      auto [vi, vv] = *viewIt;
+      assert(std::cmp_equal(index, vi));
+      assert(*baseIt == vv);
+
+      ++baseIt;
+      ++viewIt;
+    }
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/ctor.default.pass.cpp
new file mode 100644
index 00000000000000..8d1c985dbfd33a
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/ctor.default.pass.cpp
@@ -0,0 +1,81 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// class enumerate_view
+
+// constexpr enumerate_view() requires default_initializable<V>;
+
+#include <ranges>
+
+#include <cassert>
+#include <tuple>
+#include <type_traits>
+
+constexpr int buff[] = {0, 1};
+
+template <bool DefaultConstructible>
+struct DefaultConstructibleView : std::ranges::view_base {
+  constexpr DefaultConstructibleView()
+    requires DefaultConstructible
+      : begin_(buff), end_(buff + 1) {}
+
+  constexpr int const* begin() const { return begin_; }
+  constexpr int const* end() const { return end_; }
+
+  int const* begin_;
+  int const* end_;
+};
+
+static_assert(std::is_default_constructible_v<std::ranges::enumerate_view<DefaultConstructibleView<true>>>);
+static_assert(!std::is_default_constructible_v<std::ranges::enumerate_view<DefaultConstructibleView<false>>>);
+
+constexpr bool test() {
+  using EnumerateView = std::ranges::enumerate_view<DefaultConstructibleView<true>>;
+
+  {
+    EnumerateView view;
+
+    assert((*view.begin() == std::tuple<std::ranges::iterator_t<EnumerateView>::difference_type, int>{0, 0}));
+    assert((*view.end() == std::tuple<std::ranges::iterator_t<EnumerateView>::difference_type, int>{1, 1}));
+
+    auto [bi, bv] = *view.begin();
+    assert(bi == 0);
+    assert(bv == 0);
+
+    auto [ei, ev] = *view.end();
+    assert(ei == 1);
+    assert(ev == 1);
+  }
+  {
+    EnumerateView view = {};
+
+    assert((*view.begin() == std::tuple<std::ranges::iterator_t<EnumerateView>::difference_type, int>{0, 0}));
+    assert((*view.end() == std::tuple<std::ranges::iterator_t<EnumerateView>::difference_type, int>{1, 1}));
+
+    auto [bi, bv] = *view.begin();
+    assert(bi == 0);
+    assert(bv == 0);
+
+    auto [ei, ev] = *view.end();
+    assert(ei == 1);
+    assert(ev == 1);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/enable_borrowed_range.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/enable_borrowed_range.compile.pass.cpp
new file mode 100644
index 00000000000000..303f5ac063d15f
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/enable_borrowed_range.compile.pass.cpp
@@ -0,0 +1,35 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// class enumerate_view
+
+// template<class View>
+//   constexpr bool enable_borrowed_range<enumerate_view<View>>;
+
+#include <cassert>
+#include <ranges>
+
+struct NonBorrowedRange : std::ranges::view_base {
+  int* begin();
+  int* end();
+};
+
+struct BorrowedRange : std::ranges::view_base {
+  int* begin();
+  int* end();
+};
+
+template <>
+inline constexpr bool std::ranges::enable_borrowed_range<BorrowedRange> = true;
+
+static_assert(!std::ranges::borrowed_range<std::ranges::enumerate_view<NonBorrowedRange>>);
+static_assert(std::ranges::borrowed_range<std::ranges::enumerate_view<BorrowedRange>>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/end.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/end.pass.cpp
new file mode 100644
index 00000000000000..744e33d4e33b71
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/end.pass.cpp
@@ -0,0 +1,114 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// class enumerate_view
+
+// constexpr auto end() requires (!simple-view<V>);
+// constexpr auto end() const requires range-with-movable-references<const V>;
+
+#include <cassert>
+#include <concepts>
+#include <ranges>
+
+#include "test_iterators.h"
+#include "types.h"
+
+constexpr bool test() {
+  int buff[] = {1, 2, 3, 4, 5, 6, 7, 8};
+
+  // Check the return type of .end()
+  {
+    RangeView range(buff, buff + 1);
+
+    std::ranges::enumerate_view view(range);
+    using Iterator = std::ranges::iterator_t<decltype(view)>;
+    static_assert(std::same_as<Iterator, decltype(view.end())>);
+    using Sentinel = std::ranges::sentinel_t<decltype(view)>;
+    static_assert(std::same_as<Sentinel, decltype(view.end())>);
+  }
+
+  // Check the return type of .end() const
+  {
+    RangeView range(buff, buff + 1);
+
+    const std::ranges::enumerate_view view(range);
+    using Iterator = std::ranges::iterator_t<decltype(view)>;
+    static_assert(std::same_as<Iterator, decltype(view.end())>);
+    using Sentinel = std::ranges::sentinel_t<decltype(view)>;
+    static_assert(std::same_as<Sentinel, decltype(view.end())>);
+  }
+
+  // end() over an empty range
+  {
+    RangeView range(buff, buff);
+
+    std::ranges::enumerate_view view(range);
+
+    auto it = view.end();
+    assert(base(it.base()) == buff);
+    assert(it == view.end());
+
+    auto constIt = std::as_const(view).end();
+    assert(base(constIt.base()) == buff);
+    assert(constIt == std::as_const(view).end());
+  }
+
+  // end() const over an empty range
+  {
+    RangeView range(buff, buff);
+
+    const std::ranges::enumerate_view view(range);
+
+    auto it = view.end();
+    assert(base(it.base()) == buff);
+    assert(it == view.end());
+
+    auto constIt = std::as_const(view).end();
+    assert(base(constIt.base()) == buff);
+    assert(constIt == std::as_const(view).end());
+  }
+
+  // end() over an 1-element range
+  {
+    RangeView range(buff, buff + 1);
+
+    std::ranges::enumerate_view view(range);
+
+    auto it = view.end();
+    assert(base(it.base()) == buff + 1);
+
+    auto constIt = std::as_const(view).end();
+    assert(base(constIt.base()) == buff + 1);
+  }
+
+  // end() over an N-element range
+  {
+    RangeView range(buff, buff + 8);
+
+    std::ranges::enumerate_view view(range);
+
+    auto it = view.end();
+    assert(base(it.base()) == buff + 8);
+
+    auto constIt = std::as_const(view).end();
+    assert(base(constIt.base()) == buff + 8);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/arithmetic.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/arithmetic.pass.cpp
new file mode 100644
index 00000000000000..031605a7aacd85
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/arithmetic.pass.cpp
@@ -0,0 +1,127 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// class enumerate_view
+
+// class enumerate_view::iterator
+
+// constexpr iterator& operator+=(difference_type x)
+//   requires random_access_range<Base>;
+// constexpr iterator& operator-=(difference_type x)
+//   requires random_access_range<Base>;
+
+// friend constexpr iterator operator+(const iterator& x, difference_type y)
+//   requires random_access_range<Base>;
+// friend constexpr iterator operator+(difference_type x, const iterator& y)
+//   requires random_access_range<Base>;
+// friend constexpr iterator operator-(const iterator& x, difference_type y)
+//   requires random_access_range<Base>;
+// friend constexpr difference_type operator-(const iterator& x, const iterator& y);
+
+#include <ranges>
+
+#include "test_iterators.h"
+
+// Concepts
+
+template <class T, class U>
+concept CanPlus = requires(T t, U u) { t + u; };
+
+template <class T, class U>
+concept CanPlusEqual = requires(T t, U u) { t += u; };
+
+template <class T, class U>
+concept CanMinus = requires(T t, U u) { t - u; };
+
+template <class T, class U>
+concept CanMinusEqual = requires(T t, U u) { t -= u; };
+
+template <class BaseRange>
+using EnumerateIter = std::ranges::iterator_t<std::ranges::enumerate_view<BaseRange>>;
+
+using RandomAccessRange = std::ranges::subrange<int*>;
+
+// SFINAE.
+
+static_assert(std::ranges::random_access_range<RandomAccessRange>);
+static_assert(
+    std::sized_sentinel_for<std::ranges::iterator_t<RandomAccessRange>, std::ranges::iterator_t<RandomAccessRange>>);
+
+static_assert(CanPlus<EnumerateIter<RandomAccessRange>, int>);
+static_assert(CanPlus<int, EnumerateIter<RandomAccessRange>>);
+static_assert(CanPlusEqual<EnumerateIter<RandomAccessRange>, int>);
+static_assert(CanMinus<EnumerateIter<RandomAccessRange>, int>);
+static_assert(CanMinus<EnumerateIter<RandomAccessRange>, EnumerateIter<RandomAccessRange>>);
+static_assert(CanMinusEqual<EnumerateIter<RandomAccessRange>, int>);
+
+using BidirectionalRange = std::ranges::subrange<bidirectional_iterator<int*>>;
+static_assert(!std::ranges::random_access_range<BidirectionalRange>);
+static_assert(
+    !std::sized_sentinel_for<std::ranges::iterator_t<BidirectionalRange>, std::ranges::iterator_t<BidirectionalRange>>);
+
+static_assert(!CanPlus<EnumerateIter<BidirectionalRange>, int>);
+static_assert(!CanPlus<int, EnumerateIter<BidirectionalRange>>);
+static_assert(!CanPlusEqual<EnumerateIter<BidirectionalRange>, int>);
+static_assert(!CanMinus<EnumerateIter<BidirectionalRange>, int>);
+static_assert(!CanMinusEqual<EnumerateIter<BidirectionalRange>, int>);
+
+constexpr bool test() {
+  int ts[] = {1, 2, 3, 4, 5};
+
+  RandomAccessRange r{ts, ts + 5};
+  auto ev = r | std::views::enumerate;
+
+  // operator+(x, n), operator+(n,x) and operator+=
+  {
+    auto it1 = ev.begin();
+
+    auto it2 = it1 + 3;
+    assert(it2.base() == &ts[3]);
+
+    auto it3 = 3 + it1;
+    assert(it3.base() == &ts[3]);
+
+    it1 += 3;
+    assert(it1 == it2);
+    assert(it1.base() == &ts[3]);
+  }
+
+  // operator-(x, n) and operator-=
+  {
+    auto it1 = ev.end();
+
+    auto it2 = it1 - 4;
+    assert(it2.base() == &ts[1]);
+
+    it1 -= 4;
+    assert(it1 == it2);
+    assert(it1.base() == &ts[1]);
+  }
+
+  // operator-(x, y)
+  {
+    assert((ev.end() - ev.begin()) == 5);
+
+    auto it1 = ev.begin() + 2;
+    auto it2 = ev.end() - 2;
+    assert((it1 - it2) == -1);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/base.pass.cpp
new file mode 100644
index 00000000000000..b29d504a815151
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/base.pass.cpp
@@ -0,0 +1,81 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// class enumerate_view
+
+// class enumerate_view::iterator
+
+// constexpr const iterator_t<Base>& base() const & noexcept;
+// constexpr iterator_t<Base> base() &&;
+
+#include <ranges>
+
+#include <array>
+#include <cassert>
+#include <concepts>
+#include <utility>
+#include <tuple>
+
+#include "test_iterators.h"
+#include "../types.h"
+
+template <class Iterator>
+constexpr void testBase() {
+  using Sentinel          = sentinel_wrapper<Iterator>;
+  using View              = MinimalView<Iterator, Sentinel>;
+  using EnumerateView     = std::ranges::enumerate_view<View>;
+  using EnumerateIterator = std::ranges::iterator_t<EnumerateView>;
+
+  auto make_enumerate_view = [](auto begin, auto end) {
+    View view{Iterator(begin), Sentinel(Iterator(end))};
+
+    return EnumerateView(std::move(view));
+  };
+
+  std::array array{0, 1, 2, 3, 84};
+  const auto view = make_enumerate_view(array.begin(), array.end());
+
+  // Test the const& version
+  {
+    EnumerateIterator const it                          = view.begin();
+    std::same_as<const Iterator&> decltype(auto) result = it.base();
+    ASSERT_NOEXCEPT(it.base());
+    assert(base(result) == array.begin());
+  }
+
+  // Test the && version
+  {
+    EnumerateIterator it                         = view.begin();
+    std::same_as<Iterator> decltype(auto) result = std::move(it).base();
+    assert(base(result) == array.begin());
+  }
+}
+
+constexpr bool test() {
+  testBase<cpp17_input_iterator<int*>>();
+  testBase<cpp20_input_iterator<int*>>();
+  testBase<forward_iterator<int*>>();
+  testBase<bidirectional_iterator<int*>>();
+  testBase<random_access_iterator<int*>>();
+  testBase<contiguous_iterator<int*>>();
+  testBase<int*>();
+  testBase<int const*>();
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/compare.three_way.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/compare.three_way.pass.cpp
new file mode 100644
index 00000000000000..7cc00decebcab3
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/compare.three_way.pass.cpp
@@ -0,0 +1,107 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// class enumerate_view
+
+// class enumerate_view::iterator
+
+// friend constexpr strong_ordering operator<=>(const iterator& x, const iterator& y) noexcept;
+
+#include <cassert>
+#include <ranges>
+
+#include "test_iterators.h"
+#include "../types.h"
+
+constexpr void compareOperatorTest(const auto& iter1, const auto& iter2) {
+  assert(!(iter1 < iter1));
+  assert(iter1 < iter2);
+  assert(!(iter2 < iter1));
+
+  assert(iter1 <= iter1);
+  assert(iter1 <= iter2);
+  assert(!(iter2 <= iter1));
+
+  assert(!(iter1 > iter1));
+  assert(!(iter1 > iter2));
+  assert(iter2 > iter1);
+
+  assert(iter1 >= iter1);
+  assert(!(iter1 >= iter2));
+  assert(iter2 >= iter1);
+
+  assert(iter1 == iter1);
+  assert(!(iter1 == iter2));
+  assert(iter2 == iter2);
+
+  assert(!(iter1 != iter1));
+  assert(iter1 != iter2);
+  assert(!(iter2 != iter2));
+}
+
+constexpr bool test() {
+  int buff[] = {0, 1, 2, 3};
+  {
+    using View = std::ranges::enumerate_view<RangeView>;
+
+    using Iterator = std::ranges::iterator_t<View>;
+    static_assert(std::three_way_comparable<Iterator>);
+    using Subrange = std::ranges::subrange<Iterator>;
+    static_assert(std::three_way_comparable<std::ranges::iterator_t<Subrange>>);
+    using EnumerateView = std::ranges::enumerate_view<Subrange>;
+    static_assert(std::three_way_comparable<std::ranges::iterator_t<EnumerateView>>);
+
+    RangeView const range(buff, buff + 4);
+
+    std::same_as<View> decltype(auto) ev = std::views::enumerate(range);
+
+    auto it1 = ev.begin();
+    auto it2 = it1 + 1;
+
+    compareOperatorTest(it1, it2);
+
+    assert((it1 <=> it2) == std::strong_ordering::less);
+    assert((it1 <=> it1) == std::strong_ordering::equal);
+    assert((it2 <=> it2) == std::strong_ordering::equal);
+    assert((it2 <=> it1) == std::strong_ordering::greater);
+  }
+
+  // Test an old-school iterator with no operator<=>
+  {
+    using Iterator = random_access_iterator<int*>;
+    static_assert(!std::three_way_comparable<Iterator>);
+    using Subrange = std::ranges::subrange<Iterator>;
+    static_assert(!std::three_way_comparable<std::ranges::iterator_t<Subrange>>);
+    using EnumerateView = std::ranges::enumerate_view<Subrange>;
+    static_assert(std::three_way_comparable<std::ranges::iterator_t<EnumerateView>>);
+
+    auto ev  = Subrange{Iterator{buff}, Iterator{buff + 3}} | std::views::enumerate;
+    auto it1 = ev.begin();
+    auto it2 = it1 + 1;
+
+    compareOperatorTest(it1, it2);
+
+    assert((it1 <=> it2) == std::strong_ordering::less);
+    assert((it1 <=> it1) == std::strong_ordering::equal);
+    assert((it2 <=> it2) == std::strong_ordering::equal);
+    assert((it2 <=> it1) == std::strong_ordering::greater);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/ctor.convert.pass.cpp
new file mode 100644
index 00000000000000..668686884a9107
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/ctor.convert.pass.cpp
@@ -0,0 +1,84 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// class enumerate_view
+
+// class enumerate_view::iterator
+
+// constexpr iterator(iterator<!Const> i)
+//   requires Const && convertible_to<iterator_t<V>, iterator_t<Base>>;
+
+#include <ranges>
+
+#include <array>
+#include <cassert>
+#include <concepts>
+#include <utility>
+
+#include "test_iterators.h"
+#include "../types.h"
+
+template <class Iterator, class Sentinel = sentinel_wrapper<Iterator>>
+constexpr void test() {
+  using View                   = MinimalView<Iterator, Sentinel>;
+  using EnumerateView          = std::ranges::enumerate_view<View>;
+  using EnumerateIterator      = std::ranges::iterator_t<EnumerateView>;
+  using EnumerateConstIterator = std::ranges::iterator_t<const EnumerateView>;
+
+  auto make_enumerate_view = [](auto begin, auto end) {
+    View view{Iterator(begin), Sentinel(Iterator(end))};
+
+    return EnumerateView(std::move(view));
+  };
+
+  static_assert(std::is_convertible_v<EnumerateIterator, EnumerateConstIterator>);
+
+  std::array array{0, 84, 2, 3, 4};
+  auto view = make_enumerate_view(array.begin(), array.end());
+  {
+    std::same_as<EnumerateIterator> decltype(auto) it     = view.begin();
+    std::same_as<const Iterator&> decltype(auto) itResult = it.base();
+    assert(base(base(itResult)) == array.begin());
+
+    auto [index, value] = *(++it);
+    assert(index == 1);
+    assert(value == 84);
+  }
+  {
+    std::same_as<EnumerateConstIterator> decltype(auto) it = view.begin();
+    std::same_as<const Iterator&> decltype(auto) itResult  = it.base();
+    assert(base(base(itResult)) == array.begin());
+
+    auto [index, value] = *(++it);
+    assert(index == 1);
+    assert(value == 84);
+  }
+}
+
+constexpr bool tests() {
+  test<cpp17_input_iterator<int*>>();
+  test<cpp20_input_iterator<int*>>();
+  test<forward_iterator<int*>>();
+  test<bidirectional_iterator<int*>>();
+  test<random_access_iterator<int*>>();
+  test<contiguous_iterator<int*>>();
+  test<int*>();
+
+  return true;
+}
+
+int main(int, char**) {
+  tests();
+  static_assert(tests());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/ctor.default.pass.cpp
new file mode 100644
index 00000000000000..1108025de01032
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/ctor.default.pass.cpp
@@ -0,0 +1,69 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// class enumerate_view
+
+// class enumerate_view::iterator
+
+// iterator() requires default_initializable<iterator_t<Base>>;
+
+#include <cassert>
+#include <ranges>
+
+#include "test_iterators.h"
+#include "../types.h"
+
+template <class Iterator, bool IsNoexcept = true>
+constexpr void test_default_constructible() {
+  using View              = MinimalView<Iterator, sentinel_wrapper<Iterator>>;
+  using EnumerateView     = std::ranges::enumerate_view<View>;
+  using EnumerateIterator = std::ranges::iterator_t<EnumerateView>;
+
+  EnumerateIterator it1;
+  EnumerateIterator it2{};
+
+  assert(it1 == it2);
+
+  static_assert(noexcept(EnumerateIterator()) == IsNoexcept);
+}
+
+template <class Iterator>
+constexpr void test_not_default_constructible() {
+  // Make sure the iterator is *not* default constructible when the underlying iterator isn't.
+  using Sentinel          = sentinel_wrapper<Iterator>;
+  using View              = MinimalView<Iterator, Sentinel>;
+  using EnumerateView     = std::ranges::enumerate_view<View>;
+  using EnumerateIterator = std::ranges::iterator_t<EnumerateView>;
+
+  static_assert(!std::is_default_constructible_v<EnumerateIterator>);
+}
+
+constexpr bool tests() {
+  // clang-format off
+  test_not_default_constructible<cpp17_input_iterator<int*>>();
+  test_not_default_constructible<cpp20_input_iterator<int*>>();
+  test_default_constructible<forward_iterator<int*>,       /* noexcept */ false>();
+  test_default_constructible<bidirectional_iterator<int*>, /* noexcept */ false>();
+  test_default_constructible<random_access_iterator<int*>, /* noexcept */ false>();
+  test_default_constructible<contiguous_iterator<int*>,    /* noexcept */ false>();
+  test_default_constructible<int*,                         /* noexcept */ true>();
+  // clang-format on
+
+  return true;
+}
+
+int main(int, char**) {
+  tests();
+  static_assert(tests());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/deref.pass.cpp
new file mode 100644
index 00000000000000..d8e75e1634617a
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/deref.pass.cpp
@@ -0,0 +1,101 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// class enumerate_view
+
+// class enumerate_view::iterator
+
+// constexpr auto operator*() const;
+
+#include <array>
+#include <cassert>
+#include <concepts>
+#include <cstddef>
+#include <utility>
+#include <tuple>
+
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "../types.h"
+
+template <class Iterator, class ValueType = int, class Sentinel = sentinel_wrapper<Iterator>>
+constexpr void test() {
+  using View              = MinimalView<Iterator, Sentinel>;
+  using EnumerateView     = std::ranges::enumerate_view<View>;
+  using EnumerateIterator = std::ranges::iterator_t<EnumerateView>;
+
+  using Result = std::tuple<typename EnumerateIterator::difference_type,
+                            std::ranges::range_reference_t<MinimalView<Iterator, Sentinel>>>;
+
+  std::array array{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+
+  View view{Iterator(array.begin()), Sentinel(Iterator(array.end()))};
+  EnumerateView ev{std::move(view)};
+
+  {
+    auto it = ev.begin();
+    for (std::size_t index = 0; index < array.size(); ++index) {
+      std::same_as<Result> decltype(auto) result = *it;
+
+      auto [resultIndex, resultValue] = result;
+      assert(std::cmp_equal(index, resultIndex));
+      assert(array[index] == resultValue);
+
+      ++it;
+    }
+
+    assert(it == ev.end());
+  }
+
+  // const
+  {
+    auto constIt = std::as_const(ev).begin();
+    for (std::size_t index = 0; index < array.size(); ++index) {
+      std::same_as<Result> decltype(auto) result = *constIt;
+
+      auto [resultIndex, resultValue] = result;
+      assert(std::cmp_equal(index, resultIndex));
+      assert(array[index] == resultValue);
+
+      ++constIt;
+    }
+
+    assert(constIt == ev.end());
+  }
+}
+
+constexpr bool tests() {
+  test<cpp17_input_iterator<int*>>();
+  test<cpp20_input_iterator<int*>>();
+  test<forward_iterator<int*>>();
+  test<bidirectional_iterator<int*>>();
+  test<random_access_iterator<int*>>();
+  test<contiguous_iterator<int*>>();
+  test<int*>();
+
+  test<cpp17_input_iterator<int const*>, int const>();
+  test<cpp20_input_iterator<int const*>, int const>();
+  test<forward_iterator<int const*>, int const>();
+  test<bidirectional_iterator<int const*>, int const>();
+  test<random_access_iterator<int const*>, int const>();
+  test<contiguous_iterator<int const*>, int const>();
+  test<int const*, int const>();
+
+  return true;
+}
+
+int main(int, char**) {
+  tests();
+  static_assert(tests());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/equal.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/equal.pass.cpp
new file mode 100644
index 00000000000000..6bf131d356d9ca
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/equal.pass.cpp
@@ -0,0 +1,97 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// class enumerate_view
+
+// class enumerate_view::iterator
+
+// friend constexpr bool operator==(const iterator& x, const iterator& y) noexcept;
+
+#include <cassert>
+#include <ranges>
+
+#include "test_iterators.h"
+#include "../types.h"
+#include "../types_iterators.h"
+
+
+// template <bool Const>
+// struct Iterator {
+//   using value_type       = int
+//   using difference_type  = std::std::ptrdiff_t;
+//   using iterator_concept = std::input_iterator_tag;
+
+//   constexpr decltype(auto) operator*() const { return *it_; }
+//   constexpr Iterator& operator++() {
+//     ++it_;
+
+//     return *this;
+//   }
+//   constexpr void operator++(int) { ++it_; }
+
+//   std::tuple<std::ptrdiff_t, int>* it_;
+// };
+
+// template <bool Const>
+// struct Sentinel {
+//   constexpr bool operator==(const Iterator<Const>& i) const { return i.it_ == end_; }
+
+//   std::tuple<std::ptrdiff_t, int>* end_;
+// };
+
+// template <bool Const>
+// struct CrossComparableSentinel {
+//   template <bool C>
+//   constexpr bool operator==(const Iterator<C>& i) const {
+//     return i.it_ == end_;
+//   }
+
+//   std::tuple<std::ptrdiff_t, int>* end_;
+// };
+
+constexpr bool test() {
+  int buff[] = {0, 1, 2, 3, 5};
+  {
+    using View = std::ranges::enumerate_view<RangeView>;
+    RangeView const range(buff, buff + 5);
+
+    std::same_as<View> decltype(auto) ev = std::views::enumerate(range);
+
+    auto it1 = ev.begin();
+    auto it2 = it1 + 5;
+
+    assert(it1 == it1);
+    ASSERT_NOEXCEPT(it1 == it1);
+    assert(it1 != it2);
+    ASSERT_NOEXCEPT(it1 != it2);
+    assert(it2 != it1);
+    ASSERT_NOEXCEPT(it2 != it1);
+    assert(it2 == ev.end());
+    assert(ev.end() == it2);
+
+    for (std::size_t index = 0; index != 5; ++index) {
+      ++it1;
+    }
+
+    assert(it1 == it2);
+    assert(it2 == it1);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/index.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/index.pass.cpp
new file mode 100644
index 00000000000000..0256115f4a6a9d
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/index.pass.cpp
@@ -0,0 +1,95 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// class enumerate_view
+
+// class enumerate_view::iterator
+
+// constexpr difference_type index() const noexcept;
+
+#include <array>
+#include <cassert>
+#include <concepts>
+#include <cstdint>
+#include <utility>
+#include <tuple>
+
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "../types.h"
+
+template <class Iterator, class ValueType = int, class Sentinel = sentinel_wrapper<Iterator>>
+constexpr void test() {
+  using View          = MinimalView<Iterator, Sentinel>;
+  using EnumerateView = std::ranges::enumerate_view<View>;
+
+  std::array array{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+
+  View view{Iterator(array.begin()), Sentinel(Iterator(array.end()))};
+  EnumerateView ev(std::move(view));
+
+  {
+    auto it = ev.begin();
+    ASSERT_NOEXCEPT(it.index());
+
+    static_assert(std::same_as<typename decltype(it)::difference_type, decltype(it.index())>);
+    for (std::size_t index = 0; index < array.size(); ++index) {
+      assert(std::cmp_equal(index, it.index()));
+
+      ++it;
+    }
+
+    assert(it == ev.end());
+  }
+
+  // const
+  {
+    auto constIt = std::as_const(ev).begin();
+    ASSERT_NOEXCEPT(constIt.index());
+
+    static_assert(std::same_as<typename decltype(constIt)::difference_type, decltype(constIt.index())>);
+    for (std::size_t index = 0; index < array.size(); ++index) {
+      assert(std::cmp_equal(index, constIt.index()));
+
+      ++constIt;
+    }
+
+    assert(constIt == ev.end());
+  }
+}
+
+constexpr bool tests() {
+  test<cpp17_input_iterator<int*>>();
+  test<cpp20_input_iterator<int*>>();
+  test<forward_iterator<int*>>();
+  test<bidirectional_iterator<int*>>();
+  test<random_access_iterator<int*>>();
+  test<contiguous_iterator<int*>>();
+  test<int*>();
+
+  test<cpp17_input_iterator<int const*>, int const>();
+  test<cpp20_input_iterator<int const*>, int const>();
+  test<forward_iterator<int const*>, int const>();
+  test<bidirectional_iterator<int const*>, int const>();
+  test<random_access_iterator<int const*>, int const>();
+  test<contiguous_iterator<int const*>, int const>();
+  test<int const*, int const>();
+
+  return true;
+}
+
+int main(int, char**) {
+  tests();
+  static_assert(tests());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/iter_move.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/iter_move.pass.cpp
new file mode 100644
index 00000000000000..c023d58bd859b7
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/iter_move.pass.cpp
@@ -0,0 +1,75 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// class enumerate_view
+
+// class enumerate_view::iterator
+
+// friend constexpr auto iter_move(const iterator& i)
+//   noexcept(noexcept(ranges::iter_move(i.current_)) &&
+//             is_nothrow_move_constructible_v<range_rvalue_reference_t<Base>>);
+
+#include <array>
+#include <cassert>
+#include <ranges>
+#include <type_traits>
+#include <utility>
+
+#include "test_iterators.h"
+#include "../types.h"
+
+template <class Iterator, bool HasNoexceptIterMove>
+constexpr void test() {
+  using Sentinel          = sentinel_wrapper<Iterator>;
+  using View              = MinimalView<Iterator, Sentinel>;
+  using EnumerateView     = std::ranges::enumerate_view<View>;
+  using EnumerateIterator = std::ranges::iterator_t<EnumerateView>;
+
+  std::array array{0, 1, 2, 3, 4};
+
+  View view{Iterator(array.begin()), Sentinel(Iterator(array.end()))};
+  EnumerateView ev{std::move(view)};
+  EnumerateIterator const it = ev.begin();
+
+  auto&& result = iter_move(it);
+
+  static_assert(std::is_same_v<decltype(result),
+                               std::tuple<typename std::ranges::iterator_t<EnumerateView>::difference_type, int&&>&&>);
+  static_assert(std::is_same_v<decltype(result), std::tuple<typename decltype(it)::difference_type, int&&>&&>);
+
+  assert(get<0>(result) == 0);
+  assert(&get<1>(result) == array.begin());
+
+  static_assert(noexcept(iter_move(it)) == HasNoexceptIterMove);
+}
+
+constexpr bool tests() {
+  // clang-format off
+  test<cpp17_input_iterator<int*>,           /* noexcept */ false>();
+  test<cpp20_input_iterator<int*>,           /* noexcept */ false>();
+  test<forward_iterator<int*>,               /* noexcept */ false>();
+  test<bidirectional_iterator<int*>,         /* noexcept */ false>();
+  test<random_access_iterator<int*>,         /* noexcept */ false>();
+  test<contiguous_iterator<int*>,            /* noexcept */ false>();
+  test<int*,                                 /* noexcept */ true>();
+  test<MaybeNoexceptIterMoveInputIterator<true>,  /* noexcept */ true>();
+  test<MaybeNoexceptIterMoveInputIterator<false>, /* noexcept */ false>();
+  // clang-format on
+
+  return true;
+}
+
+int main(int, char**) {
+  tests();
+  static_assert(tests());
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/subscript.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/subscript.pass.cpp
new file mode 100644
index 00000000000000..d6f9d4beda938a
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/subscript.pass.cpp
@@ -0,0 +1,77 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// class enumerate_view
+
+// class enumerate_view::iterator
+
+// constexpr auto operator[](difference_type n) const
+//   requires random_access_range<Base>;
+
+#include <array>
+#include <cassert>
+#include <ranges>
+#include <tuple>
+
+#include "test_iterators.h"
+
+template <class T, class U>
+concept HasSubscriptOperator = requires(T t, U u) { t[u]; };
+
+template <class BaseRange>
+using EnumerateIterator = std::ranges::iterator_t<std::ranges::enumerate_view<BaseRange>>;
+
+using RandomAccessRange = std::ranges::subrange<int*>;
+static_assert(std::ranges::random_access_range<RandomAccessRange>);
+
+static_assert(HasSubscriptOperator<EnumerateIterator<RandomAccessRange>, int>);
+
+using BidirectionalRange = std::ranges::subrange<bidirectional_iterator<int*>>;
+static_assert(!std::ranges::random_access_range<BidirectionalRange>);
+
+static_assert(!HasSubscriptOperator<EnumerateIterator<BidirectionalRange>, int>);
+
+constexpr bool test() {
+  // Reference
+  {
+    std::array ts = {0, 1, 2, 3, 84};
+    auto view     = ts | std::views::enumerate;
+    auto it       = view.begin();
+
+    for (std::size_t index = 0; index != ts.size(); ++index) {
+      assert(it[index] == *(it + index));
+    }
+
+    static_assert(std::is_same_v<decltype(it[2]), std::tuple<decltype(it)::difference_type, int&>>);
+  }
+
+  // Value
+  {
+    auto view = std::views::iota(0, 5) | std::views::enumerate;
+    auto it   = view.begin();
+
+    for (std::size_t index = 0; index != 5; ++index) {
+      assert(it[index] == *(it + index));
+    }
+
+    static_assert(std::is_same_v<decltype(it[2]), std::tuple<decltype(it)::difference_type, int>>);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/types.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/types.compile.pass.cpp
new file mode 100644
index 00000000000000..a800e362befb0d
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/types.compile.pass.cpp
@@ -0,0 +1,112 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// class enumerate_view
+
+// class enumerate_view::iterator
+
+// std::enumerate_view::<iterator>::difference_type;
+// std::enumerate_view::<iterator>::value_type;
+// std::enumerate_view::<iterator>::iterator_category;
+// std::enumerate_view::<iterator>::iterator_concept;
+
+#include <ranges>
+
+#include <type_traits>
+#include "test_iterators.h"
+#include "../types.h"
+
+template <typename T>
+concept HasIteratorCategory = requires { typename T::iterator_category; };
+
+template <class Iterator>
+using EnumerateViewFor = std::ranges::enumerate_view< MinimalView<Iterator, sentinel_wrapper<Iterator>>>;
+
+template <class Iterator>
+using EnumerateIteratorFor = std::ranges::iterator_t<EnumerateViewFor<Iterator>>;
+
+struct ForwardIteratorWithInputCategory {
+  using difference_type   = int;
+  using value_type        = int;
+  using iterator_category = std::input_iterator_tag;
+  using iterator_concept  = std::forward_iterator_tag;
+  ForwardIteratorWithInputCategory();
+  ForwardIteratorWithInputCategory& operator++();
+  ForwardIteratorWithInputCategory operator++(int);
+  int& operator*() const;
+  friend bool operator==(ForwardIteratorWithInputCategory, ForwardIteratorWithInputCategory);
+};
+static_assert(std::forward_iterator<ForwardIteratorWithInputCategory>);
+
+constexpr bool test() {
+  // Check that value_type is range_value_t and difference_type is range_difference_t
+  {
+    auto test = []<class Iterator> {
+      using EnumerateView     = EnumerateViewFor<Iterator>;
+      using EnumerateIterator = EnumerateIteratorFor<Iterator>;
+      static_assert(std::is_same_v<typename EnumerateIterator::value_type, std::ranges::range_value_t<EnumerateView>>);
+      static_assert(
+          std::is_same_v<typename EnumerateIterator::difference_type, std::ranges::range_difference_t<EnumerateView>>);
+    };
+    test.operator()<cpp17_input_iterator<int*>>();
+    test.operator()<cpp20_input_iterator<int*>>();
+    test.operator()<forward_iterator<int*>>();
+    test.operator()<bidirectional_iterator<int*>>();
+    test.operator()<random_access_iterator<int*>>();
+    test.operator()<contiguous_iterator<int*>>();
+    test.operator()<int*>();
+  }
+  // Check iterator_concept for various categories of ranges
+  {
+    static_assert(
+        std::is_same_v<EnumerateIteratorFor<cpp17_input_iterator<int*>>::iterator_concept, std::input_iterator_tag>);
+    static_assert(
+        std::is_same_v<EnumerateIteratorFor<cpp20_input_iterator<int*>>::iterator_concept, std::input_iterator_tag>);
+    static_assert(std::is_same_v<EnumerateIteratorFor<ForwardIteratorWithInputCategory>::iterator_concept,
+                                 std::forward_iterator_tag>);
+    static_assert(
+        std::is_same_v<EnumerateIteratorFor<forward_iterator<int*>>::iterator_concept, std::forward_iterator_tag>);
+    static_assert(std::is_same_v<EnumerateIteratorFor<bidirectional_iterator<int*>>::iterator_concept,
+                                 std::bidirectional_iterator_tag>);
+    static_assert(std::is_same_v<EnumerateIteratorFor<random_access_iterator<int*>>::iterator_concept,
+                                 std::random_access_iterator_tag>);
+    static_assert(std::is_same_v<EnumerateIteratorFor<contiguous_iterator<int*>>::iterator_concept,
+                                 std::random_access_iterator_tag>);
+    static_assert(std::is_same_v<EnumerateIteratorFor<int*>::iterator_concept, std::random_access_iterator_tag>);
+  }
+
+  // Check iterator_category for various categories of ranges
+  {
+    static_assert(HasIteratorCategory<EnumerateIteratorFor<cpp17_input_iterator<int*>>>);
+    static_assert(HasIteratorCategory<EnumerateIteratorFor<cpp20_input_iterator<int*>>>);
+    static_assert(std::is_same_v<EnumerateIteratorFor<ForwardIteratorWithInputCategory>::iterator_category,
+                                 std::input_iterator_tag>);
+    static_assert(
+        std::is_same_v<EnumerateIteratorFor<forward_iterator<int*>>::iterator_category, std::input_iterator_tag>);
+    static_assert(
+        std::is_same_v<EnumerateIteratorFor<bidirectional_iterator<int*>>::iterator_category, std::input_iterator_tag>);
+    static_assert(
+        std::is_same_v<EnumerateIteratorFor<random_access_iterator<int*>>::iterator_category, std::input_iterator_tag>);
+    static_assert(
+        std::is_same_v<EnumerateIteratorFor<contiguous_iterator<int*>>::iterator_category, std::input_iterator_tag>);
+    static_assert(std::is_same_v<EnumerateIteratorFor<int*>::iterator_category, std::input_iterator_tag>);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/sentinel/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/sentinel/base.pass.cpp
new file mode 100644
index 00000000000000..28a93decfddb89
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/sentinel/base.pass.cpp
@@ -0,0 +1,62 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// class enumerate_view
+
+// class enumerate_view::sentinel
+
+// constexpr sentinel_t<Base> base() const;
+
+#include <ranges>
+
+#include <array>
+#include <cassert>
+#include <concepts>
+#include <utility>
+
+#include "test_iterators.h"
+#include "../types.h"
+
+template <class Iterator, class Sentinel = sentinel_wrapper<Iterator>>
+constexpr void test() {
+  using View              = MinimalView<Iterator, Sentinel>;
+  using EnumerateView     = std::ranges::enumerate_view<View>;
+  using EnumerateSentinel = std::ranges::sentinel_t<EnumerateView>;
+
+  std::array<int, 5> array{0, 1, 2, 3, 84};
+
+  View mv{Iterator{array.begin()}, Sentinel{Iterator{array.end()}}};
+  EnumerateView ev{std::move(mv)};
+
+  EnumerateSentinel const s                    = ev.end();
+  std::same_as<Sentinel> decltype(auto) result = s.base();
+  assert(base(base(result)) == array.end());
+}
+
+constexpr bool tests() {
+  test<cpp17_input_iterator<int*>>();
+  test<cpp20_input_iterator<int*>>();
+  test<forward_iterator<int*>>();
+  test<bidirectional_iterator<int*>>();
+  test<random_access_iterator<int*>>();
+  test<contiguous_iterator<int*>>();
+  test<int*>();
+
+  return true;
+}
+
+int main(int, char**) {
+  tests();
+  static_assert(tests());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/sentinel/ctor.convert.pass.cpp
new file mode 100644
index 00000000000000..3cdf52db41650b
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/sentinel/ctor.convert.pass.cpp
@@ -0,0 +1,75 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// class enumerate_view
+
+// class enumerate_view::sentinel
+
+//  constexpr sentinel(sentinel<!Const> other)
+//       requires Const && convertible_to<sentinel_t<V>, sentinel_t<Base>>;
+
+#include <ranges>
+
+#include <array>
+#include <cassert>
+#include <concepts>
+#include <utility>
+
+#include "test_iterators.h"
+#include "../types.h"
+
+template <class Iterator, class Sentinel = sentinel_wrapper<Iterator>>
+constexpr void test() {
+  using View                   = MinimalView<Iterator, Sentinel>;
+  using EnumerateView          = std::ranges::enumerate_view<View>;
+  using EnumerateSentinel      = std::ranges::sentinel_t<EnumerateView>;
+  using EnumerateConstSentinel = std::ranges::sentinel_t<const EnumerateView>;
+
+  auto make_enumerate_view = [](auto begin, auto end) {
+    View view{Iterator(begin), Sentinel(Iterator(end))};
+
+    return EnumerateView(std::move(view));
+  };
+
+  static_assert(std::is_convertible_v<EnumerateSentinel, EnumerateConstSentinel>);
+
+  std::array array{0, 1, 2, 3, 84};
+  auto view = make_enumerate_view(array.begin(), array.end());
+
+  std::same_as<EnumerateSentinel> decltype(auto) s = view.end();
+  std::same_as<Sentinel> decltype(auto) sResult    = s.base();
+  assert(base(base(sResult)) == array.end());
+
+  // Test assignment
+  EnumerateConstSentinel cs                      = s;
+  std::same_as<Sentinel> decltype(auto) csResult = cs.base();
+  assert(base(base(csResult)) == array.end());
+}
+
+constexpr bool tests() {
+  test<cpp17_input_iterator<int*>>();
+  test<cpp20_input_iterator<int*>>();
+  test<forward_iterator<int*>>();
+  test<bidirectional_iterator<int*>>();
+  test<random_access_iterator<int*>>();
+  test<contiguous_iterator<int*>>();
+  test<int*>();
+
+  return true;
+}
+
+int main(int, char**) {
+  tests();
+  static_assert(tests());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/sentinel/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/sentinel/ctor.default.pass.cpp
new file mode 100644
index 00000000000000..9206441d5ccf36
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/sentinel/ctor.default.pass.cpp
@@ -0,0 +1,76 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// class enumerate_view
+
+// class enumerate_view::sentinel
+
+// sentinel() = default;
+
+#include <cassert>
+#include <ranges>
+
+#include "test_iterators.h"
+#include "../types.h"
+
+struct PODSentinel {
+  int i; // deliberately uninitialised
+
+  friend constexpr bool operator==(std::tuple<int>*, const PODSentinel&) { return true; }
+};
+
+template <typename Iterator, typename Sentinel>
+struct PODSentinelView : MinimalView<Iterator, Sentinel> {
+  std::tuple<int>* begin() const;
+  PODSentinel end();
+};
+
+template <class Iterator>
+constexpr void test() {
+  using Sentinel          = sentinel_wrapper<Iterator>;
+  using View              = PODSentinelView<Iterator, Sentinel>;
+  using EnumerateView     = std::ranges::enumerate_view<View>;
+  using EnumerateSentinel = std::ranges::sentinel_t<EnumerateView>;
+
+  {
+    EnumerateSentinel s;
+
+    assert(s.base().i == 0);
+  }
+
+  {
+    EnumerateSentinel s = {};
+
+    assert(s.base().i == 0);
+  }
+
+  static_assert(noexcept(EnumerateSentinel()));
+}
+
+constexpr bool tests() {
+  test<cpp17_input_iterator<int*>>();
+  test<cpp20_input_iterator<int*>>();
+  test<forward_iterator<int*>>();
+  test<bidirectional_iterator<int*>>();
+  test<random_access_iterator<int*>>();
+  test<contiguous_iterator<int*>>();
+  test<int*>();
+
+  return true;
+}
+
+int main(int, char**) {
+  tests();
+  static_assert(tests());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/sentinel/equal.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/sentinel/equal.pass.cpp
new file mode 100644
index 00000000000000..2a502564b4a45d
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/sentinel/equal.pass.cpp
@@ -0,0 +1,71 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// class enumerate_view
+
+// class enumerate_view::sentinel
+
+// template<bool OtherConst>
+//   requires sentinel_for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>>
+// friend constexpr bool operator==(const iterator<OtherConst>& x, const sentinel& y);
+
+#include <ranges>
+
+#include <array>
+#include <cassert>
+#include <concepts>
+#include <utility>
+
+#include "test_iterators.h"
+#include "../types.h"
+
+template <class Iterator, class Sentinel = sentinel_wrapper<Iterator>>
+constexpr void test() {
+  using View = MinimalView<Iterator, Sentinel>;
+
+  std::array array{0, 1, 2, 3, 84};
+
+  View v(Iterator(array.begin()), Sentinel(Iterator(array.end())));
+  std::ranges::enumerate_view view(std::move(v));
+
+  auto const it = view.begin();
+  auto const s  = view.end();
+
+  std::same_as<bool> decltype(auto) eqItSResult = (it == s);
+  assert(!eqItSResult);
+  std::same_as<bool> decltype(auto) eqSItResult = (s == it);
+  assert(!eqSItResult);
+
+  std::same_as<bool> decltype(auto) neqItSResult = (it != s);
+  assert(neqItSResult);
+  std::same_as<bool> decltype(auto) neqSItResult = (s != it);
+  assert(neqSItResult);
+}
+
+constexpr bool tests() {
+  test<cpp17_input_iterator<int*>>();
+  test<cpp20_input_iterator<int*>>();
+  test<forward_iterator<int*>>();
+  test<bidirectional_iterator<int*>>();
+  test<random_access_iterator<int*>>();
+  test<contiguous_iterator<int*>>();
+  test<int*>();
+
+  return true;
+}
+
+int main(int, char**) {
+  tests();
+  static_assert(tests());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/sentinel/minus.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/sentinel/minus.pass.cpp
new file mode 100644
index 00000000000000..274829668c7c10
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/sentinel/minus.pass.cpp
@@ -0,0 +1,253 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// class enumerate_view
+
+// class enumerate_view::sentinel
+
+// template<bool OtherConst>
+//   requires sized_sentinel_for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>>
+// friend constexpr range_difference_t<maybe-const<OtherConst, V>>
+//   operator-(const iterator<OtherConst>& x, const sentinel& y);
+
+// template<bool OtherConst>
+//   requires sized_sentinel_for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>>
+// friend constexpr range_difference_t<maybe-const<OtherConst, V>>
+//   operator-(const sentinel& x, const iterator<OtherConst>& y);
+
+#include <array>
+#include <cassert>
+#include <ranges>
+
+#include "test_iterators.h"
+#include "../types.h"
+#include "../types_iterators.h"
+
+// template <bool Const>
+// struct Iter {
+//   int* it_;
+
+//   using value_type       = int;
+//   using difference_type  = std::ptrdiff_t;
+//   using iterator_concept = std::input_iterator_tag;
+
+//   constexpr decltype(auto) operator*() const { return *it_; }
+//   constexpr Iter& operator++() {
+//     ++it_;
+//     return *this;
+//   }
+//   constexpr void operator++(int) { ++it_; }
+// };
+
+// template <bool Const>
+// struct Sent {
+//   int* end_;
+
+//   constexpr bool operator==(const Iter<Const>& i) const { return i.it_ == end_; }
+// };
+
+// template <bool Const>
+// struct SizedSent {
+//   int* end_;
+
+//   constexpr bool operator==(const Iter<Const>& i) const { return i.it_ == end_; }
+
+//   friend constexpr auto operator-(const SizedSent& st, const Iter<Const>& it) { return st.end_ - it.it_; }
+
+//   friend constexpr auto operator-(const Iter<Const>& it, const SizedSent& st) { return it.it_ - st.end_; }
+// };
+
+// template <bool Const>
+// struct CrossSizedSent {
+//   int* end_;
+
+//   template <bool C>
+//   constexpr bool operator==(const Iter<C>& i) const {
+//     return i.it_ == end_;
+//   }
+
+//   template <bool C>
+//   friend constexpr auto operator-(const CrossSizedSent& st, const Iter<C>& it) {
+//     return st.end_ - it.it_;
+//   }
+
+//   template <bool C>
+//   friend constexpr auto operator-(const Iter<C>& it, const CrossSizedSent& st) {
+//     return it.it_ - st.end_;
+//   }
+// };
+
+// template <template <bool> class It, template <bool> class St>
+// struct BufferView : std::ranges::view_base {
+//   template <std::size_t N>
+//   constexpr BufferView(int (&b)[N]) : buffer_(b), size_(N) {}
+
+//   template <std::size_t N>
+//   constexpr BufferView(std::array<int, N>& arr) : buffer_(arr.data()), size_(N) {}
+
+//   using iterator       = It<false>;
+//   using sentinel       = St<false>;
+//   using const_iterator = It<true>;
+//   using const_sentinel = St<true>;
+
+//   constexpr iterator begin() { return {buffer_}; }
+//   constexpr const_iterator begin() const { return {buffer_}; }
+//   constexpr sentinel end() { return sentinel{buffer_ + size_}; }
+//   constexpr const_sentinel end() const { return const_sentinel{buffer_ + size_}; }
+
+//   int* buffer_;
+//   std::size_t size_;
+// };
+
+template <template <bool> class It, template <bool> class St>
+struct SizedBufferView : BufferView<It, St> {
+  constexpr std::size_t size() { return BufferView<It, St>::size_; }
+};
+
+template <class T, class U>
+concept HasMinus = requires(const T t, const U u) { t - u; };
+
+template <class BaseView>
+using EnumerateView = std::ranges::enumerate_view<BaseView>;
+
+template <class BaseView>
+using EnumerateIter = std::ranges::iterator_t<EnumerateView<BaseView>>;
+
+template <class BaseView>
+using EnumerateConstIter = std::ranges::iterator_t<const EnumerateView<BaseView>>;
+
+template <class BaseView>
+using EnumerateSentinel = std::ranges::sentinel_t<EnumerateView<BaseView>>;
+
+template <class BaseView>
+using EnumerateConstSentinel = std::ranges::sentinel_t<const EnumerateView<BaseView>>;
+
+constexpr void testConstraints() {
+  // Base is not sized
+  {
+    using Base = BufferView<Iterator, Sentinel>;
+
+    static_assert(!HasSize<Base>);
+    static_assert(!std::ranges::sized_range<Base>);
+
+    static_assert(!HasMinus<EnumerateIter<Base>, EnumerateSentinel<Base>>);
+    static_assert(!HasMinus<EnumerateIter<Base>, EnumerateConstSentinel<Base>>);
+
+    static_assert(!HasMinus<EnumerateConstIter<Base>, EnumerateSentinel<Base>>);
+    static_assert(!HasMinus<EnumerateConstIter<Base>, EnumerateConstSentinel<Base>>);
+
+    static_assert(!HasMinus<EnumerateSentinel<Base>, EnumerateIter<Base>>);
+    static_assert(!HasMinus<EnumerateSentinel<Base>, EnumerateConstIter<Base>>);
+
+    static_assert(!HasMinus<EnumerateConstSentinel<Base>, EnumerateIter<Base>>);
+    static_assert(!HasMinus<EnumerateConstSentinel<Base>, EnumerateConstIter<Base>>);
+  }
+
+  // Base is sized but not cross const
+  {
+    using Base = SizedBufferView<Iterator, SizedSentinel>;
+
+    static_assert(HasSize<Base>);
+    static_assert(std::ranges::sized_range<Base>);
+
+    static_assert(HasMinus<EnumerateIter<Base>, EnumerateSentinel<Base>>);
+    static_assert(!HasMinus<EnumerateIter<Base>, EnumerateConstSentinel<Base>>);
+
+    static_assert(!HasMinus<EnumerateConstIter<Base>, EnumerateSentinel<Base>>);
+    static_assert(HasMinus<EnumerateConstIter<Base>, EnumerateConstSentinel<Base>>);
+
+    static_assert(HasMinus<EnumerateSentinel<Base>, EnumerateIter<Base>>);
+    static_assert(!HasMinus<EnumerateSentinel<Base>, EnumerateConstIter<Base>>);
+
+    static_assert(!HasMinus<EnumerateConstSentinel<Base>, EnumerateIter<Base>>);
+    static_assert(HasMinus<EnumerateConstSentinel<Base>, EnumerateConstIter<Base>>);
+  }
+
+  // Base is cross const sized
+  {
+    using Base = BufferView<Iterator, CrossSizedSentinel>;
+
+    static_assert(!HasSize<Base>);
+    static_assert(!std::ranges::sized_range<Base>);
+
+    static_assert(HasMinus<EnumerateIter<Base>, EnumerateSentinel<Base>>);
+    static_assert(HasMinus<EnumerateIter<Base>, EnumerateConstSentinel<Base>>);
+
+    static_assert(HasMinus<EnumerateConstIter<Base>, EnumerateSentinel<Base>>);
+    static_assert(HasMinus<EnumerateConstIter<Base>, EnumerateConstSentinel<Base>>);
+
+    static_assert(HasMinus<EnumerateSentinel<Base>, EnumerateIter<Base>>);
+    static_assert(HasMinus<EnumerateSentinel<Base>, EnumerateConstIter<Base>>);
+
+    static_assert(HasMinus<EnumerateConstSentinel<Base>, EnumerateIter<Base>>);
+    static_assert(HasMinus<EnumerateConstSentinel<Base>, EnumerateConstIter<Base>>);
+  }
+}
+
+constexpr bool test() {
+  int buffer[] = {1, 2, 3, 4, 5};
+
+  // Base is sized but not cross const
+  {
+    using Base = SizedBufferView<Iterator, SizedSentinel>;
+
+    static_assert(HasSize<Base>);
+    static_assert(std::ranges::sized_range<Base>);
+
+    Base base{buffer};
+    auto ev         = base | std::views::enumerate;
+    auto iter       = ev.begin();
+    auto const_iter = std::as_const(ev).begin();
+    auto sent       = ev.end();
+    auto const_sent = std::as_const(ev).end();
+
+    // Asssert difference
+    assert(iter - sent == -5);
+    assert(sent - iter == 5);
+    assert(const_iter - const_sent == -5);
+    assert(const_sent - const_iter == 5);
+  }
+
+  // Base is cross const sized
+  {
+    using Base = BufferView<Iterator, CrossSizedSentinel>;
+
+    static_assert(!HasSize<Base>);
+    static_assert(!std::ranges::sized_range<Base>);
+
+    Base base{buffer};
+    auto ev         = base | std::views::enumerate;
+    auto iter       = ev.begin();
+    auto const_iter = std::as_const(ev).begin();
+    auto sent       = ev.end();
+    auto const_sent = std::as_const(ev).end();
+
+    // Assert difference
+    assert(iter - sent == -5);
+    assert(sent - iter == 5);
+    assert(iter - const_sent == -5);
+    assert(const_sent - iter == 5);
+    assert(const_iter - sent == -5);
+    assert(sent - const_iter == 5);
+    assert(const_iter - const_sent == -5);
+    assert(const_sent - const_iter == 5);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/size.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/size.pass.cpp
new file mode 100644
index 00000000000000..9be911c34959a0
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/size.pass.cpp
@@ -0,0 +1,54 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// class enumerate_view
+
+// constexpr auto size() requires sized_range<V>;
+// constexpr auto size() const requires sized_range<const V>;
+
+#include <cassert>
+#include <ranges>
+
+#include "test_iterators.h"
+#include "types.h"
+
+struct NonSizedRangeView : std::ranges::view_base {
+  using iterator = forward_iterator<int*>;
+  iterator begin() const;
+  iterator end() const;
+};
+
+static_assert(!std::ranges::sized_range<NonSizedRangeView>);
+static_assert(!std::ranges::sized_range<const NonSizedRangeView>);
+
+static_assert(!HasSize<std::ranges::enumerate_view<NonSizedRangeView>>);
+static_assert(!HasSize<const std::ranges::enumerate_view<NonSizedRangeView>>);
+
+constexpr bool test() {
+  int buffer[] = {1, 2, 3};
+
+  // Non-const and const are sized
+  {
+    auto view = std::views::enumerate(buffer);
+    assert(view.size() == 3);
+    assert(std::as_const(view).size() == 3);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/types.h b/libcxx/test/std/ranges/range.adaptors/range.enumerate/types.h
new file mode 100644
index 00000000000000..1d55107df98cd7
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/types.h
@@ -0,0 +1,136 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_ENUMERATE_TYPES_H
+#define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_ENUMERATE_TYPES_H
+
+#include <ranges>
+
+#include "test_iterators.h"
+#include "test_macros.h"
+
+// Types
+
+struct RangeView : std::ranges::view_base {
+  using Iterator = cpp20_input_iterator<int*>;
+  using Sentinel = sentinel_wrapper<Iterator>;
+
+  constexpr explicit RangeView(int* b, int* e) : begin_(b), end_(e) {}
+  constexpr RangeView(RangeView const& other) : begin_(other.begin_), end_(other.end_), wasCopyInitialized(true) {}
+  constexpr RangeView(RangeView&& other) : begin_(other.begin_), end_(other.end_), wasMoveInitialized(true) {}
+  RangeView& operator=(RangeView const&) = default;
+  RangeView& operator=(RangeView&&)      = default;
+
+  constexpr int* begin() const { return begin_; }
+  constexpr int* end() const { return end_; }
+
+  int* begin_;
+  int* end_;
+
+  bool wasCopyInitialized = false;
+  bool wasMoveInitialized = false;
+};
+
+LIBCPP_STATIC_ASSERT(std::ranges::__range_with_movable_references<RangeView>);
+static_assert(std::ranges::range<RangeView>);
+static_assert(std::ranges::view<RangeView>);
+
+struct MinimalDefaultConstructedView : std::ranges::view_base {
+  MinimalDefaultConstructedView() = default;
+
+  forward_iterator<int*> begin() const;
+  sentinel_wrapper<forward_iterator<int*>> end() const;
+};
+
+static_assert(std::ranges::view<MinimalDefaultConstructedView>);
+
+template <class Iterator, class Sentinel>
+struct MinimalView : std::ranges::view_base {
+  constexpr explicit MinimalView(Iterator it, Sentinel sent) : it_(base(std::move(it))), sent_(base(std::move(sent))) {}
+
+  MinimalView(MinimalView&&)            = default;
+  MinimalView& operator=(MinimalView&&) = default;
+
+  constexpr Iterator begin() const { return Iterator(it_); }
+  constexpr Sentinel end() const { return Sentinel(sent_); }
+
+private:
+  decltype(base(std::declval<Iterator>())) it_;
+  decltype(base(std::declval<Sentinel>())) sent_;
+};
+
+static_assert(
+    std::ranges::view< MinimalView<cpp17_input_iterator<int*>, sentinel_wrapper<cpp17_input_iterator<int*>>>>);
+
+struct NotInvocable {};
+
+static_assert(!std::invocable<NotInvocable>);
+
+struct NotAView {};
+
+static_assert(!std::ranges::view<NotAView>);
+
+struct NotAViewRange {
+  using Iterator = cpp20_input_iterator<int*>;
+  using Sentinel = sentinel_wrapper<Iterator>;
+
+  NotAViewRange() = default;
+  constexpr explicit NotAViewRange(int* b, int* e) : begin_(b), end_(e) {}
+  constexpr NotAViewRange(NotAViewRange const& other) = default;
+  constexpr NotAViewRange(NotAViewRange&& other)      = default;
+  NotAViewRange& operator=(NotAViewRange const&)      = default;
+  NotAViewRange& operator=(NotAViewRange&&)           = default;
+
+  constexpr int* begin() const { return begin_; }
+  constexpr int* end() const { return end_; }
+
+  int* begin_;
+  int* end_;
+};
+
+static_assert(std::ranges::range<NotAViewRange>);
+static_assert(!std::ranges::view<NotAViewRange>);
+
+template <bool IsNoexcept>
+class MaybeNoexceptIterMoveInputIterator {
+  int* it_;
+
+public:
+  using iterator_category = std::input_iterator_tag;
+  using value_type        = int;
+  using difference_type   = typename std::iterator_traits<int*>::difference_type;
+  using pointer           = int*;
+  using reference         = int&;
+
+  MaybeNoexceptIterMoveInputIterator() = default;
+  explicit constexpr MaybeNoexceptIterMoveInputIterator(int* it) : it_(it) {}
+
+  friend constexpr decltype(auto) iter_move(const MaybeNoexceptIterMoveInputIterator& it) noexcept(IsNoexcept) {
+    return std::ranges::iter_move(it.it_);
+  }
+
+  friend constexpr int* base(const MaybeNoexceptIterMoveInputIterator& i) { return i.it_; }
+
+  constexpr reference operator*() const { return *it_; }
+  constexpr MaybeNoexceptIterMoveInputIterator& operator++() {
+    ++it_;
+    return *this;
+  }
+  constexpr MaybeNoexceptIterMoveInputIterator operator++(int) {
+    MaybeNoexceptIterMoveInputIterator tmp(*this);
+    ++(*this);
+    return tmp;
+  }
+};
+
+// Concepts
+
+template <class T>
+concept HasSize = requires(T t) { t.size(); };
+
+#endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_ENUMERATE_TYPES_H
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/types_iterators.h b/libcxx/test/std/ranges/range.adaptors/range.enumerate/types_iterators.h
new file mode 100644
index 00000000000000..231e8f68dbb93a
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/types_iterators.h
@@ -0,0 +1,109 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_ENUMERATE_TYPES_ITERATORS_H
+#define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_ENUMERATE_TYPES_ITERATORS_H
+
+#include "types.h"
+
+// Iterators & Sentinels
+
+template <bool Const>
+struct Iterator {
+  using value_type       = int;
+  using difference_type  = std::ptrdiff_t;
+  using iterator_concept = std::input_iterator_tag;
+
+  constexpr decltype(auto) operator*() const { return *it_; }
+  constexpr Iterator& operator++() {
+    ++it_;
+
+    return *this;
+  }
+  constexpr void operator++(int) { ++it_; }
+
+  std::tuple<std::ptrdiff_t, int>* it_;
+};
+
+template <bool Const>
+struct Sentinel {
+  constexpr bool operator==(const Iterator<Const>& i) const { return i.it_ == end_; }
+
+  std::tuple<std::ptrdiff_t, int>* end_;
+};
+
+template <bool Const>
+struct CrossComparableSentinel {
+  template <bool C>
+  constexpr bool operator==(const Iterator<C>& i) const {
+    return i.it_ == end_;
+  }
+
+  std::tuple<std::ptrdiff_t, int>* end_;
+};
+
+template <bool Const>
+struct SizedSentinel {
+  constexpr bool operator==(const Iterator<Const>& i) const { return i.it_ == end_; }
+
+  friend constexpr auto operator-(const SizedSentinel& st, const Iterator<Const>& it) { return st.end_ - it.it_; }
+
+  friend constexpr auto operator-(const Iterator<Const>& it, const SizedSentinel& st) { return it.it_ - st.end_; }
+
+  int* end_;
+};
+
+template <bool Const>
+struct CrossSizedSentinel {
+  template <bool C>
+  constexpr bool operator==(const Iterator<C>& i) const {
+    return i.it_ == end_;
+  }
+
+  template <bool C>
+  friend constexpr auto operator-(const CrossSizedSentinel& st, const Iterator<C>& it) {
+    return st.end_ - it.it_;
+  }
+
+  template <bool C>
+  friend constexpr auto operator-(const Iterator<C>& it, const CrossSizedSentinel& st) {
+    return it.it_ - st.end_;
+  }
+
+  int* end_;
+};
+
+// Views
+
+template <template <bool> class It, template <bool> class St>
+struct BufferView : std::ranges::view_base {
+  template <std::size_t N>
+  constexpr BufferView(int (&b)[N]) : buffer_(b), size_(N) {}
+
+  template <std::size_t N>
+  constexpr BufferView(std::array<int, N>& arr) : buffer_(arr.data()), size_(N) {}
+
+  using iterator       = It<false>;
+  using sentinel       = St<false>;
+  using const_iterator = It<true>;
+  using const_sentinel = St<true>;
+
+  using difference_type = int;
+  using iterator_type   = std::tuple<std::ptrdiff_t, int>;
+
+  constexpr iterator begin() { return iterator_type{pos_, buffer_}; }
+  constexpr const_iterator begin() const { return {iterator_type{pos_, buffer_}}; }
+  constexpr sentinel end() { return sentinel{buffer_ + size_}; }
+  constexpr const_sentinel end() const { return const_sentinel{pos_, buffer_ + size_}; }
+
+  std::ptrdiff_t pos_;
+  int* buffer_;
+  std::size_t size_;
+};
+
+#endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_ENUMERATE_TYPES_ITERATORS_H
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index 3a068ab120b159..25a93c5fc71440 100755
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -911,6 +911,11 @@ def add_version_header(tc):
             "values": {"c++23": 202202},
             "headers": ["ranges"],
         },
+        {
+            "name": "__cpp_lib_ranges_enumerate",
+            "values": {"c++23": 202302},
+            "headers": ["ranges"],
+        },
         {
             "name": "__cpp_lib_ranges_iota",
             "values": {"c++23": 202202},

>From eb53557181eb01db265e25d66fdf8f6fd357f98f Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Sun, 31 Dec 2023 12:16:26 +0200
Subject: [PATCH 2/5] WIP: Commented out improperly completed tests temporarily

---
 .../range.enumerate/iterator/equal.pass.cpp   |   3 +-
 .../range.enumerate/sentinel/minus.pass.cpp   | 219 +++++++++---------
 2 files changed, 111 insertions(+), 111 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/equal.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/equal.pass.cpp
index 6bf131d356d9ca..38a0d303d47be1 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/equal.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/equal.pass.cpp
@@ -21,8 +21,7 @@
 
 #include "test_iterators.h"
 #include "../types.h"
-#include "../types_iterators.h"
-
+// #include "../types_iterators.h"
 
 // template <bool Const>
 // struct Iterator {
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/sentinel/minus.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/sentinel/minus.pass.cpp
index 274829668c7c10..1f88fd1a183432 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.enumerate/sentinel/minus.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/sentinel/minus.pass.cpp
@@ -30,7 +30,7 @@
 
 #include "test_iterators.h"
 #include "../types.h"
-#include "../types_iterators.h"
+// #include "../types_iterators.h"
 
 // template <bool Const>
 // struct Iter {
@@ -86,27 +86,27 @@
 //   }
 // };
 
-// template <template <bool> class It, template <bool> class St>
-// struct BufferView : std::ranges::view_base {
-//   template <std::size_t N>
-//   constexpr BufferView(int (&b)[N]) : buffer_(b), size_(N) {}
+template <template <bool> class It, template <bool> class St>
+struct BufferView : std::ranges::view_base {
+  template <std::size_t N>
+  constexpr BufferView(int (&b)[N]) : buffer_(b), size_(N) {}
 
-//   template <std::size_t N>
-//   constexpr BufferView(std::array<int, N>& arr) : buffer_(arr.data()), size_(N) {}
+  template <std::size_t N>
+  constexpr BufferView(std::array<int, N>& arr) : buffer_(arr.data()), size_(N) {}
 
-//   using iterator       = It<false>;
-//   using sentinel       = St<false>;
-//   using const_iterator = It<true>;
-//   using const_sentinel = St<true>;
+  using iterator       = It<false>;
+  using sentinel       = St<false>;
+  using const_iterator = It<true>;
+  using const_sentinel = St<true>;
 
-//   constexpr iterator begin() { return {buffer_}; }
-//   constexpr const_iterator begin() const { return {buffer_}; }
-//   constexpr sentinel end() { return sentinel{buffer_ + size_}; }
-//   constexpr const_sentinel end() const { return const_sentinel{buffer_ + size_}; }
+  constexpr iterator begin() { return {buffer_}; }
+  constexpr const_iterator begin() const { return {buffer_}; }
+  constexpr sentinel end() { return sentinel{buffer_ + size_}; }
+  constexpr const_sentinel end() const { return const_sentinel{buffer_ + size_}; }
 
-//   int* buffer_;
-//   std::size_t size_;
-// };
+  int* buffer_;
+  std::size_t size_;
+};
 
 template <template <bool> class It, template <bool> class St>
 struct SizedBufferView : BufferView<It, St> {
@@ -131,116 +131,117 @@ using EnumerateSentinel = std::ranges::sentinel_t<EnumerateView<BaseView>>;
 template <class BaseView>
 using EnumerateConstSentinel = std::ranges::sentinel_t<const EnumerateView<BaseView>>;
 
-constexpr void testConstraints() {
-  // Base is not sized
-  {
-    using Base = BufferView<Iterator, Sentinel>;
+// template <typename Iterator, typename Sentinel>
+// constexpr void testConstraints() {
+//   // Base is not sized
+//   {
+//     using Base = BufferView<Iterator, Sentinel>;
 
-    static_assert(!HasSize<Base>);
-    static_assert(!std::ranges::sized_range<Base>);
+//     static_assert(!HasSize<Base>);
+//     static_assert(!std::ranges::sized_range<Base>);
 
-    static_assert(!HasMinus<EnumerateIter<Base>, EnumerateSentinel<Base>>);
-    static_assert(!HasMinus<EnumerateIter<Base>, EnumerateConstSentinel<Base>>);
+//     static_assert(!HasMinus<EnumerateIter<Base>, EnumerateSentinel<Base>>);
+//     static_assert(!HasMinus<EnumerateIter<Base>, EnumerateConstSentinel<Base>>);
 
-    static_assert(!HasMinus<EnumerateConstIter<Base>, EnumerateSentinel<Base>>);
-    static_assert(!HasMinus<EnumerateConstIter<Base>, EnumerateConstSentinel<Base>>);
+//     static_assert(!HasMinus<EnumerateConstIter<Base>, EnumerateSentinel<Base>>);
+//     static_assert(!HasMinus<EnumerateConstIter<Base>, EnumerateConstSentinel<Base>>);
 
-    static_assert(!HasMinus<EnumerateSentinel<Base>, EnumerateIter<Base>>);
-    static_assert(!HasMinus<EnumerateSentinel<Base>, EnumerateConstIter<Base>>);
+//     static_assert(!HasMinus<EnumerateSentinel<Base>, EnumerateIter<Base>>);
+//     static_assert(!HasMinus<EnumerateSentinel<Base>, EnumerateConstIter<Base>>);
 
-    static_assert(!HasMinus<EnumerateConstSentinel<Base>, EnumerateIter<Base>>);
-    static_assert(!HasMinus<EnumerateConstSentinel<Base>, EnumerateConstIter<Base>>);
-  }
+//     static_assert(!HasMinus<EnumerateConstSentinel<Base>, EnumerateIter<Base>>);
+//     static_assert(!HasMinus<EnumerateConstSentinel<Base>, EnumerateConstIter<Base>>);
+//   }
 
-  // Base is sized but not cross const
-  {
-    using Base = SizedBufferView<Iterator, SizedSentinel>;
+//   // Base is sized but not cross const
+//   {
+//     using Base = SizedBufferView<Iterator, SizedSentinel>;
 
-    static_assert(HasSize<Base>);
-    static_assert(std::ranges::sized_range<Base>);
+//     static_assert(HasSize<Base>);
+//     static_assert(std::ranges::sized_range<Base>);
 
-    static_assert(HasMinus<EnumerateIter<Base>, EnumerateSentinel<Base>>);
-    static_assert(!HasMinus<EnumerateIter<Base>, EnumerateConstSentinel<Base>>);
+//     static_assert(HasMinus<EnumerateIter<Base>, EnumerateSentinel<Base>>);
+//     static_assert(!HasMinus<EnumerateIter<Base>, EnumerateConstSentinel<Base>>);
 
-    static_assert(!HasMinus<EnumerateConstIter<Base>, EnumerateSentinel<Base>>);
-    static_assert(HasMinus<EnumerateConstIter<Base>, EnumerateConstSentinel<Base>>);
+//     static_assert(!HasMinus<EnumerateConstIter<Base>, EnumerateSentinel<Base>>);
+//     static_assert(HasMinus<EnumerateConstIter<Base>, EnumerateConstSentinel<Base>>);
 
-    static_assert(HasMinus<EnumerateSentinel<Base>, EnumerateIter<Base>>);
-    static_assert(!HasMinus<EnumerateSentinel<Base>, EnumerateConstIter<Base>>);
+//     static_assert(HasMinus<EnumerateSentinel<Base>, EnumerateIter<Base>>);
+//     static_assert(!HasMinus<EnumerateSentinel<Base>, EnumerateConstIter<Base>>);
 
-    static_assert(!HasMinus<EnumerateConstSentinel<Base>, EnumerateIter<Base>>);
-    static_assert(HasMinus<EnumerateConstSentinel<Base>, EnumerateConstIter<Base>>);
-  }
+//     static_assert(!HasMinus<EnumerateConstSentinel<Base>, EnumerateIter<Base>>);
+//     static_assert(HasMinus<EnumerateConstSentinel<Base>, EnumerateConstIter<Base>>);
+//   }
 
-  // Base is cross const sized
-  {
-    using Base = BufferView<Iterator, CrossSizedSentinel>;
+//   // Base is cross const sized
+//   {
+//     using Base = BufferView<Iterator, CrossSizedSentinel>;
 
-    static_assert(!HasSize<Base>);
-    static_assert(!std::ranges::sized_range<Base>);
+//     static_assert(!HasSize<Base>);
+//     static_assert(!std::ranges::sized_range<Base>);
 
-    static_assert(HasMinus<EnumerateIter<Base>, EnumerateSentinel<Base>>);
-    static_assert(HasMinus<EnumerateIter<Base>, EnumerateConstSentinel<Base>>);
+//     static_assert(HasMinus<EnumerateIter<Base>, EnumerateSentinel<Base>>);
+//     static_assert(HasMinus<EnumerateIter<Base>, EnumerateConstSentinel<Base>>);
 
-    static_assert(HasMinus<EnumerateConstIter<Base>, EnumerateSentinel<Base>>);
-    static_assert(HasMinus<EnumerateConstIter<Base>, EnumerateConstSentinel<Base>>);
+//     static_assert(HasMinus<EnumerateConstIter<Base>, EnumerateSentinel<Base>>);
+//     static_assert(HasMinus<EnumerateConstIter<Base>, EnumerateConstSentinel<Base>>);
 
-    static_assert(HasMinus<EnumerateSentinel<Base>, EnumerateIter<Base>>);
-    static_assert(HasMinus<EnumerateSentinel<Base>, EnumerateConstIter<Base>>);
+//     static_assert(HasMinus<EnumerateSentinel<Base>, EnumerateIter<Base>>);
+//     static_assert(HasMinus<EnumerateSentinel<Base>, EnumerateConstIter<Base>>);
 
-    static_assert(HasMinus<EnumerateConstSentinel<Base>, EnumerateIter<Base>>);
-    static_assert(HasMinus<EnumerateConstSentinel<Base>, EnumerateConstIter<Base>>);
-  }
-}
+//     static_assert(HasMinus<EnumerateConstSentinel<Base>, EnumerateIter<Base>>);
+//     static_assert(HasMinus<EnumerateConstSentinel<Base>, EnumerateConstIter<Base>>);
+//   }
+// }
 
 constexpr bool test() {
-  int buffer[] = {1, 2, 3, 4, 5};
-
-  // Base is sized but not cross const
-  {
-    using Base = SizedBufferView<Iterator, SizedSentinel>;
-
-    static_assert(HasSize<Base>);
-    static_assert(std::ranges::sized_range<Base>);
-
-    Base base{buffer};
-    auto ev         = base | std::views::enumerate;
-    auto iter       = ev.begin();
-    auto const_iter = std::as_const(ev).begin();
-    auto sent       = ev.end();
-    auto const_sent = std::as_const(ev).end();
-
-    // Asssert difference
-    assert(iter - sent == -5);
-    assert(sent - iter == 5);
-    assert(const_iter - const_sent == -5);
-    assert(const_sent - const_iter == 5);
-  }
-
-  // Base is cross const sized
-  {
-    using Base = BufferView<Iterator, CrossSizedSentinel>;
-
-    static_assert(!HasSize<Base>);
-    static_assert(!std::ranges::sized_range<Base>);
-
-    Base base{buffer};
-    auto ev         = base | std::views::enumerate;
-    auto iter       = ev.begin();
-    auto const_iter = std::as_const(ev).begin();
-    auto sent       = ev.end();
-    auto const_sent = std::as_const(ev).end();
-
-    // Assert difference
-    assert(iter - sent == -5);
-    assert(sent - iter == 5);
-    assert(iter - const_sent == -5);
-    assert(const_sent - iter == 5);
-    assert(const_iter - sent == -5);
-    assert(sent - const_iter == 5);
-    assert(const_iter - const_sent == -5);
-    assert(const_sent - const_iter == 5);
-  }
+  // int buffer[] = {1, 2, 3, 4, 5};
+
+  // // Base is sized but not cross const
+  // {
+  //   using Base = SizedBufferView<Iterator, SizedSentinel>;
+
+  //   static_assert(HasSize<Base>);
+  //   static_assert(std::ranges::sized_range<Base>);
+
+  //   Base base{buffer};
+  //   auto ev         = base | std::views::enumerate;
+  //   auto iter       = ev.begin();
+  //   auto const_iter = std::as_const(ev).begin();
+  //   auto sent       = ev.end();
+  //   auto const_sent = std::as_const(ev).end();
+
+  //   // Asssert difference
+  //   assert(iter - sent == -5);
+  //   assert(sent - iter == 5);
+  //   assert(const_iter - const_sent == -5);
+  //   assert(const_sent - const_iter == 5);
+  // }
+
+  // // Base is cross const sized
+  // {
+  //   using Base = BufferView<Iterator, CrossSizedSentinel>;
+
+  //   static_assert(!HasSize<Base>);
+  //   static_assert(!std::ranges::sized_range<Base>);
+
+  //   Base base{buffer};
+  //   auto ev         = base | std::views::enumerate;
+  //   auto iter       = ev.begin();
+  //   auto const_iter = std::as_const(ev).begin();
+  //   auto sent       = ev.end();
+  //   auto const_sent = std::as_const(ev).end();
+
+  //   // Assert difference
+  //   assert(iter - sent == -5);
+  //   assert(sent - iter == 5);
+  //   assert(iter - const_sent == -5);
+  //   assert(const_sent - iter == 5);
+  //   assert(const_iter - sent == -5);
+  //   assert(sent - const_iter == 5);
+  //   assert(const_iter - const_sent == -5);
+  //   assert(const_sent - const_iter == 5);
+  // }
 
   return true;
 }

>From 8f922d1b5d4f84ff0bbca3e1e4fe46514fcb88e3 Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Sun, 31 Dec 2023 12:50:03 +0200
Subject: [PATCH 3/5] WIP: Addressed comments

---
 .../range.adaptors/range.enumerate/begin.pass.cpp    |  2 +-
 .../range.enumerate/iterator/types.compile.pass.cpp  | 12 ++----------
 .../ranges/range.adaptors/range.enumerate/types.h    |  1 -
 3 files changed, 3 insertions(+), 12 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/begin.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/begin.pass.cpp
index 64876ed71355f1..c8ca13350fa75a 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.enumerate/begin.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/begin.pass.cpp
@@ -100,7 +100,7 @@ constexpr bool test() {
     assert(it == view.end());
   }
 
-  // begin() over an 1-element range
+  // begin() over a 1-element range
   {
     RangeView range(buff, buff + 1);
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/types.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/types.compile.pass.cpp
index a800e362befb0d..07e4adfa240645 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/types.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/types.compile.pass.cpp
@@ -47,7 +47,7 @@ struct ForwardIteratorWithInputCategory {
 };
 static_assert(std::forward_iterator<ForwardIteratorWithInputCategory>);
 
-constexpr bool test() {
+constexpr void test() {
   // Check that value_type is range_value_t and difference_type is range_difference_t
   {
     auto test = []<class Iterator> {
@@ -65,6 +65,7 @@ constexpr bool test() {
     test.operator()<contiguous_iterator<int*>>();
     test.operator()<int*>();
   }
+
   // Check iterator_concept for various categories of ranges
   {
     static_assert(
@@ -100,13 +101,4 @@ constexpr bool test() {
         std::is_same_v<EnumerateIteratorFor<contiguous_iterator<int*>>::iterator_category, std::input_iterator_tag>);
     static_assert(std::is_same_v<EnumerateIteratorFor<int*>::iterator_category, std::input_iterator_tag>);
   }
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
 }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/types.h b/libcxx/test/std/ranges/range.adaptors/range.enumerate/types.h
index 1d55107df98cd7..cf90094c0006e6 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.enumerate/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/types.h
@@ -37,7 +37,6 @@ struct RangeView : std::ranges::view_base {
 };
 
 LIBCPP_STATIC_ASSERT(std::ranges::__range_with_movable_references<RangeView>);
-static_assert(std::ranges::range<RangeView>);
 static_assert(std::ranges::view<RangeView>);
 
 struct MinimalDefaultConstructedView : std::ranges::view_base {

>From 9a9f5d3404a30adf43ad0534e4487c84b6d359ea Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Mon, 1 Jan 2024 15:13:08 +0200
Subject: [PATCH 4/5] WIP: nothing

---
 .../range.enumerate/iterator/base.pass.cpp    | 57 +++++++++++++++++++
 1 file changed, 57 insertions(+)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/base.pass.cpp
index b29d504a815151..261ede8eb8ddd2 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/base.pass.cpp
@@ -28,6 +28,55 @@
 #include "test_iterators.h"
 #include "../types.h"
 
+// template <class It, class ItTraits = It>
+// class MovableIterator {
+//   using Traits = std::iterator_traits<ItTraits>;
+//   It it_;
+
+//   template <class U, class T>
+//   friend class MovableIterator;
+
+// public:
+//   using iterator_category = std::input_iterator_tag;
+//   using value_type        = typename Traits::value_type;
+//   using difference_type   = typename Traits::difference_type;
+//   using pointer           = It;
+//   using reference         = typename Traits::reference;
+
+//   TEST_CONSTEXPR explicit MovableIterator(It it) : it_(it), justInitialized{true} { static_assert(false); }
+
+//   template <class U, class T>
+//   TEST_CONSTEXPR MovableIterator(const MovableIterator<U, T>& u) : it_(u.it_), wasCopyInitialized{true} {
+//     static_assert(false);
+//   }
+
+//   template <class U, class T, class = typename std::enable_if<std::is_default_constructible<U>::value>::type>
+//   TEST_CONSTEXPR_CXX14 MovableIterator(MovableIterator<U, T>&& u) : it_(u.it_), wasMoveInitialized{true} {
+//     static_assert(false);
+//     u.it_ = U();
+//   }
+
+//   TEST_CONSTEXPR reference operator*() const { return *it_; }
+
+//   TEST_CONSTEXPR_CXX14 MovableIterator& operator++() {
+//     ++it_;
+//     return *this;
+//   }
+//   TEST_CONSTEXPR_CXX14 MovableIterator operator++(int) { return MovableIterator(it_++); }
+
+//   friend TEST_CONSTEXPR bool operator==(const MovableIterator& x, const MovableIterator& y) { return x.it_ == y.it_; }
+//   friend TEST_CONSTEXPR bool operator!=(const MovableIterator& x, const MovableIterator& y) { return x.it_ != y.it_; }
+
+//   friend TEST_CONSTEXPR It base(const MovableIterator& i) { return i.it_; }
+
+//   template <class T>
+//   void operator,(T const&) = delete;
+
+//   bool justInitialized    = false;
+//   bool wasCopyInitialized = false;
+//   bool wasMoveInitialized = false;
+// };
+
 template <class Iterator>
 constexpr void testBase() {
   using Sentinel          = sentinel_wrapper<Iterator>;
@@ -57,10 +106,18 @@ constexpr void testBase() {
     EnumerateIterator it                         = view.begin();
     std::same_as<Iterator> decltype(auto) result = std::move(it).base();
     assert(base(result) == array.begin());
+
+    // // Test move
+    // if constexpr (std::same_as<Iterator, MovableIterator<int*>>) {
+    //   assert(result.justInitialized);
+    //   assert(!result.wasCopyInitialized);
+    //   assert(!result.wasMoveInitialized);
+    // }
   }
 }
 
 constexpr bool test() {
+  // testBase<MovableIterator<int*>>();
   testBase<cpp17_input_iterator<int*>>();
   testBase<cpp20_input_iterator<int*>>();
   testBase<forward_iterator<int*>>();

>From 3b5f9f326c0e0cbbdac25cd75413731a84bc2b55 Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Mon, 1 Jan 2024 16:01:30 +0200
Subject: [PATCH 5/5] WIP: Address comment in `adaptor.pass.cpp`

---
 .../ranges/range.adaptors/range.enumerate/adaptor.pass.cpp    | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/adaptor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/adaptor.pass.cpp
index edc58536d68dcd..fe556351387c83 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.enumerate/adaptor.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/adaptor.pass.cpp
@@ -56,6 +56,10 @@ using NonEnumeratableRangeWithImmmovabaleReferences =
 
 static_assert(std::ranges::input_range<NonEnumeratableRangeWithImmmovabaleReferences>);
 static_assert(!CanEnumerate<NonEnumeratableRangeWithImmmovabaleReferences>);
+static_assert(
+    !std::move_constructible<std::ranges::range_reference_t< NonEnumeratableRangeWithImmmovabaleReferences >>);
+static_assert(
+    !std::move_constructible<std::ranges::range_rvalue_reference_t< NonEnumeratableRangeWithImmmovabaleReferences >>);
 
 template <typename View, typename T>
 using ExpectedViewElement = std::tuple<typename std::ranges::iterator_t<View>::difference_type, T>;



More information about the flang-commits mailing list