[libcxx-commits] [libcxx] [libc++][ranges] Fix missing `forward` in `views::enumerate` (PR #197635)

Daniel Christian Mandolang via libcxx-commits libcxx-commits at lists.llvm.org
Thu May 14 21:06:19 PDT 2026


https://github.com/danielcm585 updated https://github.com/llvm/llvm-project/pull/197635

>From bb7fb16ff14fadd4f3668c3d432ebf0bbf58c1ed Mon Sep 17 00:00:00 2001
From: danielcm585 <danielchristianmandolang at gmail.com>
Date: Thu, 14 May 2026 16:26:24 +0800
Subject: [PATCH 1/2] [libc++][ranges] Fix missing forward

---
 libcxx/include/__ranges/enumerate_view.h      |  6 ++--
 .../range.enumerate/adaptor.pass.cpp          | 35 +++++++++++++++++++
 2 files changed, 38 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__ranges/enumerate_view.h b/libcxx/include/__ranges/enumerate_view.h
index 3ba103b34b916..d6412422f76b0 100644
--- a/libcxx/include/__ranges/enumerate_view.h
+++ b/libcxx/include/__ranges/enumerate_view.h
@@ -325,9 +325,9 @@ namespace __enumerate {
 struct __fn : __range_adaptor_closure<__fn> {
   template <class _Range>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto
-  operator()(_Range&& __range) noexcept(noexcept(/**/ enumerate_view<views::all_t<_Range>>(__range)))
-      -> decltype(/*-------------------------------*/ enumerate_view<views::all_t<_Range>>(__range)) {
-    return /*--------------------------------------*/ enumerate_view<views::all_t<_Range>>(__range);
+  operator()(_Range&& __range) noexcept(noexcept(/**/ enumerate_view<views::all_t<_Range>>(std::forward<_Range>(__range))))
+      -> decltype(/*-------------------------------*/ enumerate_view<views::all_t<_Range>>(std::forward<_Range>(__range))) {
+    return /*--------------------------------------*/ enumerate_view<views::all_t<_Range>>(std::forward<_Range>(__range));
   }
 };
 
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 7d87959b24a89..2452b75a45041 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
@@ -90,6 +90,28 @@ 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)>);
 
+struct MoveOnlyView : std::ranges::view_base {
+  constexpr explicit MoveOnlyView(int* b, int* e) : begin_(b), end_(e) {}
+  MoveOnlyView(const MoveOnlyView&)            = delete;
+  MoveOnlyView& operator=(const MoveOnlyView&) = delete;
+  constexpr MoveOnlyView(MoveOnlyView&& other) noexcept
+      : begin_(std::exchange(other.begin_, nullptr)), end_(std::exchange(other.end_, nullptr)) {}
+  MoveOnlyView& operator=(MoveOnlyView&&) = default;
+
+  constexpr int* begin() const { return begin_; }
+  constexpr int* end() const { return end_; }
+
+  int* begin_ = nullptr;
+  int* end_   = nullptr;
+};
+
+static_assert(std::ranges::view<MoveOnlyView>);
+static_assert(!std::copy_constructible<MoveOnlyView>);
+static_assert(!std::is_invocable_v<decltype(std::views::enumerate), MoveOnlyView&>);
+static_assert(!std::is_invocable_v<decltype(std::views::enumerate), const MoveOnlyView&>);
+static_assert(std::is_invocable_v<decltype(std::views::enumerate), MoveOnlyView>);
+static_assert(!std::is_invocable_v<decltype(std::views::enumerate), const MoveOnlyView>);
+
 constexpr bool test() {
   // Test `views::enumerate_view(v)`
   {
@@ -127,6 +149,19 @@ constexpr bool test() {
     compareViews(result, {{0, 'b'}, {1, 'a'}, {2, 'b'}, {3, 'a'}, {4, 'z'}, {5, 'm'}, {6, 't'}});
   }
 
+  {
+    int buff[]{2, 3, 5, 7};
+    using Result = std::ranges::enumerate_view<MoveOnlyView>;
+    {
+      std::same_as<Result> decltype(auto) result = std::views::enumerate(MoveOnlyView(buff, buff + 4));
+      compareViews(std::move(result), {{0, 2}, {1, 3}, {2, 5}, {3, 7}});
+    }
+    {
+      std::same_as<Result> decltype(auto) result = MoveOnlyView(buff, buff + 4) | std::views::enumerate;
+      compareViews(std::move(result), {{0, 2}, {1, 3}, {2, 5}, {3, 7}});
+    }
+  }
+
   return true;
 }
 

>From 7267385ccc3b5da6312e6133797eda923943ff04 Mon Sep 17 00:00:00 2001
From: danielcm585 <danielchristianmandolang at gmail.com>
Date: Fri, 15 May 2026 12:06:03 +0800
Subject: [PATCH 2/2] clang-format

---
 libcxx/include/__ranges/enumerate_view.h | 102 ++++++++---------------
 1 file changed, 37 insertions(+), 65 deletions(-)

diff --git a/libcxx/include/__ranges/enumerate_view.h b/libcxx/include/__ranges/enumerate_view.h
index d6412422f76b0..1041871c6b8b1 100644
--- a/libcxx/include/__ranges/enumerate_view.h
+++ b/libcxx/include/__ranges/enumerate_view.h
@@ -45,8 +45,7 @@ namespace ranges {
 // [concept.object]
 
 template <class _Rp>
-concept __range_with_movable_references =
-    input_range<_Rp> && std::move_constructible<range_reference_t<_Rp>> &&
+concept __range_with_movable_references = input_range<_Rp> && std::move_constructible<range_reference_t<_Rp>> &&
     std::move_constructible<range_rvalue_reference_t<_Rp>>;
 
 // [range.enumerate.view]
@@ -65,53 +64,39 @@ class enumerate_view : public view_interface<enumerate_view<_View>> {
   class __sentinel;
 
 public:
-  _LIBCPP_HIDE_FROM_ABI constexpr enumerate_view()
-    requires default_initializable<_View>
+  _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)) {}
 
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin()
-    requires(!__simple_view<_View>)
-  {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() requires(!__simple_view<_View>) {
     return __iterator<false>(ranges::begin(__base_), 0);
   }
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
-    requires __range_with_movable_references<const _View>
-  {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto
+  begin() const requires __range_with_movable_references<const _View> {
     return __iterator<true>(ranges::begin(__base_), 0);
   }
 
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end()
-    requires(!__simple_view<_View>)
-  {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() requires(!__simple_view<_View>) {
     if constexpr (forward_range<_View> && common_range<_View> && sized_range<_View>)
       return __iterator<false>(ranges::end(__base_), ranges::distance(__base_));
     else
       return __sentinel<false>(ranges::end(__base_));
   }
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
-    requires __range_with_movable_references<const _View>
-  {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const requires __range_with_movable_references<const _View> {
     if constexpr (forward_range<_View> && common_range<const _View> && sized_range<const _View>)
       return __iterator<true>(ranges::end(__base_), ranges::distance(__base_));
     else
       return __sentinel<true>(ranges::end(__base_));
   }
 
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
-    requires sized_range<_View>
-  {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() requires sized_range<_View> {
     return ranges::size(__base_);
   }
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
-    requires sized_range<const _View>
-  {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const requires sized_range<const _View> {
     return ranges::size(__base_);
   }
 
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
-    requires copy_constructible<_View>
-  {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() const& requires copy_constructible<_View> {
     return __base_;
   }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
@@ -158,12 +143,11 @@ class enumerate_view<_View>::__iterator {
       : __current_(std::move(__current)), __pos_(__pos) {}
 
 public:
-  _LIBCPP_HIDE_FROM_ABI __iterator()
-    requires default_initializable<iterator_t<_Base>>
+  _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>>
+  _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_) {}
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const iterator_t<_Base>& base() const& noexcept { return __current_; }
@@ -182,49 +166,38 @@ class enumerate_view<_View>::__iterator {
 
   _LIBCPP_HIDE_FROM_ABI constexpr void operator++(int) { return ++*this; }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator++(int)
-    requires forward_range<_Base>
-  {
+  _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>
-  {
+  _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>
-  {
+  _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>
-  {
+  _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>
-  {
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator-=(difference_type __n) requires random_access_range<_Base> {
     __current_ -= __n;
     __pos_ -= __n;
     return *this;
   }
 
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator[](difference_type __n) const
-    requires random_access_range<_Base>
-  {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto
+  operator[](difference_type __n) const requires random_access_range<_Base> {
     return __reference_type(__pos_ + __n, __current_[__n]);
   }
 
@@ -237,23 +210,20 @@ class enumerate_view<_View>::__iterator {
     return __x.__pos_ <=> __y.__pos_;
   }
 
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(const __iterator& __i, difference_type __n)
-    requires random_access_range<_Base>
-  {
+  [[nodiscard]] _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;
   }
 
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(difference_type __n, const __iterator& __i)
-    requires random_access_range<_Base>
-  {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator
+  operator+(difference_type __n, const __iterator& __i) requires random_access_range<_Base> {
     return __i + __n;
   }
 
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator-(const __iterator& __i, difference_type __n)
-    requires random_access_range<_Base>
-  {
+  [[nodiscard]] _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;
@@ -287,15 +257,15 @@ class enumerate_view<_View>::__sentinel {
 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(__sentinel<!_Const> __other) requires _Const
+      && convertible_to<sentinel_t<_View>, sentinel_t<_Base>> : __end_(std::move(__other.__end_)) {}
 
   [[nodiscard]] _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) {
+  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 __x.__current_ == __y.__end_;
   }
 
@@ -324,10 +294,12 @@ namespace __enumerate {
 
 struct __fn : __range_adaptor_closure<__fn> {
   template <class _Range>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto
-  operator()(_Range&& __range) noexcept(noexcept(/**/ enumerate_view<views::all_t<_Range>>(std::forward<_Range>(__range))))
-      -> decltype(/*-------------------------------*/ enumerate_view<views::all_t<_Range>>(std::forward<_Range>(__range))) {
-    return /*--------------------------------------*/ enumerate_view<views::all_t<_Range>>(std::forward<_Range>(__range));
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto operator()(_Range&& __range) noexcept(
+      noexcept(/**/ enumerate_view<views::all_t<_Range>>(std::forward<_Range>(__range))))
+      -> decltype(/*-------------------------------*/ enumerate_view<views::all_t<_Range>>(
+          std::forward<_Range>(__range))) {
+    return /*--------------------------------------*/ enumerate_view<views::all_t<_Range>>(
+        std::forward<_Range>(__range));
   }
 };
 



More information about the libcxx-commits mailing list