[libcxx-commits] [libcxx] b232970 - [libc++][ranges] Updated `[[nodiscard]]` implementation for `subrange` and `join_with_view` (#176936)

via libcxx-commits libcxx-commits at lists.llvm.org
Tue Jan 27 01:03:12 PST 2026


Author: Hristo Hristov
Date: 2026-01-27T11:03:07+02:00
New Revision: b232970172dd0df0c8ab6c8147b6bf81a477fa03

URL: https://github.com/llvm/llvm-project/commit/b232970172dd0df0c8ab6c8147b6bf81a477fa03
DIFF: https://github.com/llvm/llvm-project/commit/b232970172dd0df0c8ab6c8147b6bf81a477fa03.diff

LOG: [libc++][ranges] Updated `[[nodiscard]]` implementation for `subrange` and `join_with_view` (#176936)

Added or removed `[[nodiscard]]` according to the guidelines and updated
the tests.

 - https://libcxx.llvm.org/CodingGuidelines.html
 - https://wg21.link/range.subrange
 -  https://wg21.link/range.join.with.view

Towards #172124

Added: 
    libcxx/test/libcxx/ranges/range.utility/range.subrange/nodiscard.verify.cpp

Modified: 
    libcxx/include/__ranges/join_with_view.h
    libcxx/include/__ranges/subrange.h
    libcxx/test/libcxx/ranges/range.adaptors/range.join.with/nodiscard.verify.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/include/__ranges/join_with_view.h b/libcxx/include/__ranges/join_with_view.h
index 8ed989a664687..3ce9011762e26 100644
--- a/libcxx/include/__ranges/join_with_view.h
+++ b/libcxx/include/__ranges/join_with_view.h
@@ -372,7 +372,7 @@ struct join_with_view<_View, _Pattern>::__iterator
     return __tmp;
   }
 
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __x, const __iterator& __y)
+  _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __x, const __iterator& __y)
     requires __ref_is_glvalue && forward_range<_Base> && equality_comparable<_InnerIter>
   {
     return __x.__outer_it_ == __y.__outer_it_ && __x.__inner_it_ == __y.__inner_it_;
@@ -420,8 +420,7 @@ struct join_with_view<_View, _Pattern>::__sentinel {
 
   template <bool _OtherConst>
     requires sentinel_for<sentinel_t<_Base>, iterator_t<__maybe_const<_OtherConst, _View>>>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool
-  operator==(const __iterator<_OtherConst>& __x, const __sentinel& __y) {
+  _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator<_OtherConst>& __x, const __sentinel& __y) {
     return __get_outer_of(__x) == __y.__end_;
   }
 };

diff  --git a/libcxx/include/__ranges/subrange.h b/libcxx/include/__ranges/subrange.h
index 87e1991a1f3b9..3d94b0a72b963 100644
--- a/libcxx/include/__ranges/subrange.h
+++ b/libcxx/include/__ranges/subrange.h
@@ -131,7 +131,7 @@ class subrange : public view_interface<subrange<_Iter, _Sent, _Kind>> {
     return _Pair(__begin_, __end_);
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr _Iter begin() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Iter begin() const
     requires copyable<_Iter>
   {
     return __begin_;
@@ -143,11 +143,11 @@ class subrange : public view_interface<subrange<_Iter, _Sent, _Kind>> {
     return std::move(__begin_);
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr _Sent end() const { return __end_; }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Sent end() const { return __end_; }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr bool empty() const { return __begin_ == __end_; }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr make_unsigned_t<iter_
diff erence_t<_Iter>> size() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr make_unsigned_t<iter_
diff erence_t<_Iter>> size() const
     requires(_Kind == subrange_kind::sized)
   {
     if constexpr (_StoreSize)
@@ -214,7 +214,7 @@ subrange(_Range&&, make_unsigned_t<range_
diff erence_t<_Range>>)
 
 template <size_t _Index, class _Iter, class _Sent, subrange_kind _Kind>
   requires((_Index == 0 && copyable<_Iter>) || _Index == 1)
-_LIBCPP_HIDE_FROM_ABI constexpr auto get(const subrange<_Iter, _Sent, _Kind>& __subrange) {
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto get(const subrange<_Iter, _Sent, _Kind>& __subrange) {
   if constexpr (_Index == 0)
     return __subrange.begin();
   else
@@ -223,7 +223,7 @@ _LIBCPP_HIDE_FROM_ABI constexpr auto get(const subrange<_Iter, _Sent, _Kind>& __
 
 template <size_t _Index, class _Iter, class _Sent, subrange_kind _Kind>
   requires(_Index < 2)
-_LIBCPP_HIDE_FROM_ABI constexpr auto get(subrange<_Iter, _Sent, _Kind>&& __subrange) {
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto get(subrange<_Iter, _Sent, _Kind>&& __subrange) {
   if constexpr (_Index == 0)
     return __subrange.begin();
   else

diff  --git a/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/nodiscard.verify.cpp
index dc2a9f5074ae4..f94992c443265 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/nodiscard.verify.cpp
@@ -10,7 +10,6 @@
 
 // Test that functions are marked [[nodiscard]].
 
-#include <array>
 #include <ranges>
 #include <utility>
 
@@ -23,77 +22,37 @@ void test() {
 
   std::ranges::join_with_view view(range, pattern);
 
-  // clang-format off
-  view.base(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::as_const(view).base(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::move(std::as_const(view)).base(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::move(view).base(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-  // clang-format on
-
-  // clang-format off
-  view.begin(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::as_const(view).begin(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-  // clang-format on
-
-  // clang-format off
-  view.end(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::as_const(view).end(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-  // clang-format on
-}
+  // [range.join.with.view]
 
-void test_iterator() {
-  char range[3][2] = {{'x', 'x'}, {'y', 'y'}, {'z', 'z'}};
-  char pattern[2]  = {',', ' '};
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(view).base();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::move(view).base();
 
-  std::ranges::join_with_view view(range, pattern);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  view.begin();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(view).begin();
 
-  // clang-format off
-  *view.begin(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-  *std::as_const(view).begin(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-  // clang-format on
-
-  // clang-format off
-  (view.begin() == view.end()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-  (std::as_const(view).begin() == view.end()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-  (view.begin() == std::as_const(view).end()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-  (std::as_const(view).begin() == std::as_const(view).end()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-  // clang-format on
-
-  // clang-format off
-  iter_move(view.begin()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-  iter_move(std::as_const(view).begin()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-  // clang-format on
-}
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  view.end();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(view).end();
 
-void test_sentinel() {
-  std::array<test_range<cpp20_input_iterator>, 0> range;
-  std::array<int, 0> pattern;
+  // [range.join.with.iterator]
 
-  std::ranges::join_with_view view(range, pattern);
-  static_assert(!std::ranges::common_range<decltype(view)>);
-
-  // clang-format off
-  (view.begin() == view.end()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-  (std::as_const(view).begin() == view.end()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-  (view.begin() == std::as_const(view).end()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-  (std::as_const(view).begin() == std::as_const(view).end()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-  // clang-format on
-}
+  auto cIt = std::as_const(view).begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  *cIt;
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  iter_move(cIt);
+
+  // [range.join.with.overview]
 
-void test_overview() {
-  int range[3][3]     = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
-  int pattern_base[2] = {-1, -1};
-  auto pattern        = std::views::all(pattern_base);
-
-  // clang-format off
-  std::views::join_with(pattern); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::join_with(range, pattern); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-  range | std::views::join_with(pattern); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::reverse | std::views::join_with(pattern); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-
-  std::views::join_with(0); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::join_with(range, 0); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-  range | std::views::join_with(0); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::reverse | std::views::join_with(0); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-  // clang-format on
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::join_with(range, pattern);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::join_with(pattern);
 }

diff  --git a/libcxx/test/libcxx/ranges/range.utility/range.subrange/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.utility/range.subrange/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..9ef94873b26eb
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.utility/range.subrange/nodiscard.verify.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++20
+
+// Test that functions are marked [[nodiscard]].
+
+#include <ranges>
+#include <utility>
+#include <vector>
+
+struct MoveOnlyIterator {
+  using iterator_concept = std::input_iterator_tag;
+  using 
diff erence_type  = std::ptr
diff _t;
+  using value_type       = int;
+
+  MoveOnlyIterator() = default;
+
+  MoveOnlyIterator(MoveOnlyIterator&&)            = default;
+  MoveOnlyIterator& operator=(MoveOnlyIterator&&) = default;
+
+  MoveOnlyIterator(const MoveOnlyIterator&)            = delete;
+  MoveOnlyIterator& operator=(const MoveOnlyIterator&) = delete;
+
+  int operator*() const;
+
+  MoveOnlyIterator& operator++();
+
+  void operator++(int);
+
+  friend bool operator==(const MoveOnlyIterator&, std::default_sentinel_t);
+};
+static_assert(std::input_iterator<MoveOnlyIterator>);
+static_assert(!std::copyable<MoveOnlyIterator>);
+
+void test() {
+  std::vector<int> range;
+  std::ranges::subrange subrange{range.begin(), range.end()};
+
+  MoveOnlyIterator it;
+  auto moveOnlySubrange = std::ranges::subrange(std::move(it), std::default_sentinel);
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(subrange).begin();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::move(moveOnlySubrange).begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(subrange).end();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(subrange).empty();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(subrange).size();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(subrange).next();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::move(subrange).next();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(subrange).prev();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::move(subrange).prev();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::get<0>(std::as_const(subrange));
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::get<0>(std::move(subrange));
+}


        


More information about the libcxx-commits mailing list