[libcxx-commits] [libcxx] [libc++][ranges] Applied [[nodiscard]] to `elements_view` (PR #206589)

Lucas Mellone via libcxx-commits libcxx-commits at lists.llvm.org
Sat Jul 4 13:44:00 PDT 2026


https://github.com/lknknm updated https://github.com/llvm/llvm-project/pull/206589

>From fde2218d5a8d799e9c2806a51ddd9758c2efd13e Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Mon, 29 Jun 2026 23:30:56 +0200
Subject: [PATCH 1/7] add: nodiscard to elements_view

---
 libcxx/include/__ranges/elements_view.h | 40 ++++++++++++-------------
 1 file changed, 20 insertions(+), 20 deletions(-)

diff --git a/libcxx/include/__ranges/elements_view.h b/libcxx/include/__ranges/elements_view.h
index b1419f2a1dd91..fc8c46b29b57f 100644
--- a/libcxx/include/__ranges/elements_view.h
+++ b/libcxx/include/__ranges/elements_view.h
@@ -77,57 +77,57 @@ class elements_view : public view_interface<elements_view<_View, _Np>> {
 
   _LIBCPP_HIDE_FROM_ABI constexpr explicit elements_view(_View __base) : __base_(std::move(__base)) {}
 
-  _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
+  [[nodiscard]] _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_); }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto begin()
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin()
     requires(!__simple_view<_View>)
   {
     return __iterator</*_Const=*/false>(ranges::begin(__base_));
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
     requires range<const _View>
   {
     return __iterator</*_Const=*/true>(ranges::begin(__base_));
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto end()
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end()
     requires(!__simple_view<_View> && !common_range<_View>)
   {
     return __sentinel</*_Const=*/false>{ranges::end(__base_)};
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto end()
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end()
     requires(!__simple_view<_View> && common_range<_View>)
   {
     return __iterator</*_Const=*/false>{ranges::end(__base_)};
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
     requires range<const _View>
   {
     return __sentinel</*_Const=*/true>{ranges::end(__base_)};
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
     requires common_range<const _View>
   {
     return __iterator</*_Const=*/true>{ranges::end(__base_)};
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto size()
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
     requires sized_range<_View>
   {
     return ranges::size(__base_);
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
     requires sized_range<const _View>
   {
     return ranges::size(__base_);
@@ -211,11 +211,11 @@ class elements_view<_View, _Np>::__iterator
     requires _Const && convertible_to<iterator_t<_View>, iterator_t<_Base>>
       : __current_(std::move(__i.__current_)) {}
 
-  _LIBCPP_HIDE_FROM_ABI constexpr const iterator_t<_Base>& base() const& noexcept { return __current_; }
+  [[nodiscard]] _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_); }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_Base> base() && { return std::move(__current_); }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const { return __get_element(__current_); }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const { return __get_element(__current_); }
 
   _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator++() {
     ++__current_;
@@ -261,7 +261,7 @@ class elements_view<_View, _Np>::__iterator
     return *this;
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator[](difference_type __n) const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator[](difference_type __n) const
     requires random_access_range<_Base>
   {
     return __get_element(__current_ + __n);
@@ -303,25 +303,25 @@ class elements_view<_View, _Np>::__iterator
     return __x.__current_ <=> __y.__current_;
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(const __iterator& __x, difference_type __y)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(const __iterator& __x, difference_type __y)
     requires random_access_range<_Base>
   {
     return __iterator{__x} += __y;
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(difference_type __x, const __iterator& __y)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(difference_type __x, const __iterator& __y)
     requires random_access_range<_Base>
   {
     return __y + __x;
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator-(const __iterator& __x, difference_type __y)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator-(const __iterator& __x, difference_type __y)
     requires random_access_range<_Base>
   {
     return __iterator{__x} -= __y;
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type operator-(const __iterator& __x, const __iterator& __y)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type operator-(const __iterator& __x, const __iterator& __y)
     requires sized_sentinel_for<iterator_t<_Base>, iterator_t<_Base>>
   {
     return __x.__current_ - __y.__current_;
@@ -365,14 +365,14 @@ class elements_view<_View, _Np>::__sentinel {
 
   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>>
+  [[nodiscard]] _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>>
+  [[nodiscard]] _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);
   }

>From 4c961f7efb3efb1985259cd16b13fae3c23ef871 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Mon, 29 Jun 2026 23:36:42 +0200
Subject: [PATCH 2/7] fix: clang format

---
 libcxx/include/__ranges/elements_view.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__ranges/elements_view.h b/libcxx/include/__ranges/elements_view.h
index fc8c46b29b57f..33c152bbd03b4 100644
--- a/libcxx/include/__ranges/elements_view.h
+++ b/libcxx/include/__ranges/elements_view.h
@@ -321,7 +321,8 @@ class elements_view<_View, _Np>::__iterator
     return __iterator{__x} -= __y;
   }
 
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type operator-(const __iterator& __x, const __iterator& __y)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(const __iterator& __x, const __iterator& __y)
     requires sized_sentinel_for<iterator_t<_Base>, iterator_t<_Base>>
   {
     return __x.__current_ - __y.__current_;

>From 8c1bb7ca3ce494344d17e1f41fd8317efe3809da Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 30 Jun 2026 12:46:41 +0200
Subject: [PATCH 3/7] add: range.elements.overview as per the 25.7.23.1.2.1
 example in the Standard

---
 .../range.elements/nodiscard.verify.cpp       | 44 +++++++++++++++++++
 1 file changed, 44 insertions(+)
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..16759e6a53569
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp
@@ -0,0 +1,44 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++20
+
+// Test the libc++ extension that std::ranges::transform_view and std::views::transform are marked as [[nodiscard]].
+
+#include <ranges>
+#include <utility>
+#include <functional>
+#include <map>
+
+struct View : std::ranges::view_interface<View> {
+  int* begin();
+  const int* begin() const;
+  volatile int* end();
+  const volatile int* end() const;
+};
+static_assert(!std::ranges::common_range<View>);
+static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator_t<const View>>);
+static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
+
+void test() {
+    auto historical_figures = std::map{
+      std::pair{"Lovelace"sv, 1815},
+      {"Turing"sv, 1912},
+      {"Babbage"sv, 1791},
+      {"Hamilton"sv, 1936}
+    };
+
+    // [range.elements.overview]
+    
+   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::views::elements<0>(historical_figures);
+
+   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::views::elements<1>(historical_figures);
+}
+

>From 77071141de6b3af76d1e8d846b887be981bbdb5e Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 30 Jun 2026 21:59:05 +0200
Subject: [PATCH 4/7] fix: drop sv

---
 .../range.adaptors/range.elements/nodiscard.verify.cpp   | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp
index 16759e6a53569..5951a04e31d91 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp
@@ -13,6 +13,7 @@
 #include <ranges>
 #include <utility>
 #include <functional>
+#include <string_view>
 #include <map>
 
 struct View : std::ranges::view_interface<View> {
@@ -27,10 +28,10 @@ static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel
 
 void test() {
     auto historical_figures = std::map{
-      std::pair{"Lovelace"sv, 1815},
-      {"Turing"sv, 1912},
-      {"Babbage"sv, 1791},
-      {"Hamilton"sv, 1936}
+        std::pair{"Lovelace", 1815},
+        std::pair{"Turing", 1912},
+        std::pair{"Babbage", 1791},
+        std::pair{"Hamilton", 1936}
     };
 
     // [range.elements.overview]

>From 6b4b2c0585ec53caefab4562a91ecbd4dd51b9ad Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 30 Jun 2026 22:35:49 +0200
Subject: [PATCH 5/7] add: full tests for range.elements

---
 .../range.elements/nodiscard.verify.cpp       | 99 ++++++++++++++-----
 1 file changed, 77 insertions(+), 22 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp
index 5951a04e31d91..0f25b2449cf5f 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp
@@ -10,36 +10,91 @@
 
 // Test the libc++ extension that std::ranges::transform_view and std::views::transform are marked as [[nodiscard]].
 
-#include <ranges>
-#include <utility>
 #include <functional>
-#include <string_view>
 #include <map>
+#include <ranges>
+#include <utility>
 
 struct View : std::ranges::view_interface<View> {
-  int* begin();
-  const int* begin() const;
-  volatile int* end();
-  const volatile int* end() const;
+  std::tuple<int, int>* begin();
+  const std::tuple<int, int>* begin() const;
+  volatile std::tuple<int, int>* end();
+  const volatile std::tuple<int, int>* end() const;
 };
 static_assert(!std::ranges::common_range<View>);
 static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator_t<const View>>);
 static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
 
 void test() {
-    auto historical_figures = std::map{
-        std::pair{"Lovelace", 1815},
-        std::pair{"Turing", 1912},
-        std::pair{"Babbage", 1791},
-        std::pair{"Hamilton", 1936}
-    };
-
-    // [range.elements.overview]
-    
-   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-    std::views::elements<0>(historical_figures);
-
-   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-    std::views::elements<1>(historical_figures);
-}
+  auto v = View{} | std::views::elements<1>;
+
+  // [range.elements.view]
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).base();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::move(v).base();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.begin();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.end();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).end();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.size();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).size();
+
+  // [range.elements.iterator]
 
+  auto it   = v.begin();
+  auto c_it = std::as_const(v).begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  c_it.base();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::move(it).base();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  *c_it;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  c_it[0];
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it + 0;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  0 + it;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it - 0;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it - it;
+
+  // [range.elements.sentinel]
+
+  auto st = v.end();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it - st;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  st - it;
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  st - c_it;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  c_it - st;
+
+  // [range.elements.overview]
+
+  auto historical_figures = std::map{
+      std::pair{"Lovelace", 1815}, std::pair{"Turing", 1912}, std::pair{"Babbage", 1791}, std::pair{"Hamilton", 1936}};
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::elements<0>(historical_figures);
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::elements<1>(historical_figures);
+}

>From 720ef3917f62f01a6c667c4fb37e1547ef96106b Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Wed, 1 Jul 2026 20:24:25 +0200
Subject: [PATCH 6/7] Update
 libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp

Co-authored-by: A. Jiang <de34 at live.cn>
---
 .../ranges/range.adaptors/range.elements/nodiscard.verify.cpp   | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp
index 0f25b2449cf5f..84790ffe90875 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp
@@ -8,7 +8,7 @@
 
 // REQUIRES: std-at-least-c++20
 
-// Test the libc++ extension that std::ranges::transform_view and std::views::transform are marked as [[nodiscard]].
+// Test the libc++ extension that std::ranges::elements_view and std::views::elements are marked as [[nodiscard]].
 
 #include <functional>
 #include <map>

>From 813282415c33ecf47816cfabfc8a980d4143f4b5 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 4 Jul 2026 22:32:36 +0200
Subject: [PATCH 7/7] add: model CommonView

---
 .../range.elements/nodiscard.verify.cpp        | 18 +++++++++++++++++-
 1 file changed, 17 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp
index 84790ffe90875..088c1dba5af9d 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp
@@ -25,8 +25,19 @@ static_assert(!std::ranges::common_range<View>);
 static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator_t<const View>>);
 static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
 
+struct CommonView : std::ranges::view_interface<View> {
+  std::tuple<int, int>* begin();
+  const std::tuple<int, int>* begin() const;
+  std::tuple<int, int>* end();
+  const std::tuple<int, int>* end() const;
+};
+static_assert(std::ranges::common_range<CommonView>);
+static_assert(!std::same_as<std::ranges::iterator_t<CommonView>, std::ranges::iterator_t<const CommonView>>);
+static_assert(!std::same_as<std::ranges::sentinel_t<CommonView>, std::ranges::sentinel_t<const CommonView>>);
+
 void test() {
-  auto v = View{} | std::views::elements<1>;
+  auto v        = View{} | std::views::elements<1>;
+  auto common_v = CommonView{} | std::views::elements<1>;
 
   // [range.elements.view]
 
@@ -45,6 +56,11 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(v).end();
 
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  common_v.end();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(common_v).end();
+
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   v.size();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}



More information about the libcxx-commits mailing list