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

Hristo Hristov via libcxx-commits libcxx-commits at lists.llvm.org
Mon Jul 6 00:14:24 PDT 2026


https://github.com/H-G-Hristov created https://github.com/llvm/llvm-project/pull/207667

`[[nodiscard]]` should be applied to functions where discarding the return value is most likely a correctness issue.

- https://libcxx.llvm.org/CodingGuidelines.html
- https://wg21.link/ranges.zip

Towards #172124

>From 33e16582ed6dcc20864c817ae0e660052916ed65 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 21 Jan 2026 13:44:27 +0200
Subject: [PATCH] [libc++][ranges] Applied `[[nodiscard]]` to `zip_view`

`[[nodiscard]]` should be applied to functions where discarding the return value is most likely a correctness issue.

- https://libcxx.llvm.org/CodingGuidelines.html
- https://wg21.link/ranges.zip

Towards #172124
---
 libcxx/include/__ranges/zip_view.h            | 33 ++++---
 .../range.zip/nodiscard.verify.cpp            | 92 +++++++++++++++++++
 2 files changed, 110 insertions(+), 15 deletions(-)
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.zip/nodiscard.verify.cpp

diff --git a/libcxx/include/__ranges/zip_view.h b/libcxx/include/__ranges/zip_view.h
index bea64c4997a9e..aa13bed5f428d 100644
--- a/libcxx/include/__ranges/zip_view.h
+++ b/libcxx/include/__ranges/zip_view.h
@@ -134,19 +134,19 @@ class zip_view : public view_interface<zip_view<_Views...>> {
 
   _LIBCPP_HIDE_FROM_ABI constexpr explicit zip_view(_Views... __views) : __views_(std::move(__views)...) {}
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto begin()
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin()
     requires(!(__simple_view<_Views> && ...))
   {
     return __iterator<false>(std::__tuple_transform(ranges::begin, __views_));
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
     requires(range<const _Views> && ...)
   {
     return __iterator<true>(std::__tuple_transform(ranges::begin, __views_));
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto end()
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end()
     requires(!(__simple_view<_Views> && ...))
   {
     if constexpr (!__zip_is_common<_Views...>) {
@@ -158,7 +158,7 @@ class zip_view : public view_interface<zip_view<_Views...>> {
     }
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
     requires(range<const _Views> && ...)
   {
     if constexpr (!__zip_is_common<const _Views...>) {
@@ -170,7 +170,7 @@ class zip_view : public view_interface<zip_view<_Views...>> {
     }
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto size()
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
     requires(sized_range<_Views> && ...)
   {
     return std::apply(
@@ -181,7 +181,7 @@ class zip_view : public view_interface<zip_view<_Views...>> {
         std::__tuple_transform(ranges::size, __views_));
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
     requires(sized_range<const _Views> && ...)
   {
     return std::apply(
@@ -267,7 +267,7 @@ class zip_view<_Views...>::__iterator : public __zip_view_iterator_category_base
     requires _Const && (convertible_to<iterator_t<_Views>, iterator_t<__maybe_const<_Const, _Views>>> && ...)
       : __current_(std::move(__i.__current_)) {}
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto operator*() const {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator*() const {
     return std::__tuple_transform([](auto& __i) -> decltype(auto) { return *__i; }, __current_);
   }
 
@@ -315,7 +315,7 @@ class zip_view<_Views...>::__iterator : public __zip_view_iterator_category_base
     return *this;
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto operator[](difference_type __n) const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator[](difference_type __n) const
     requires __zip_all_random_access<_Const, _Views...>
   {
     return std::__tuple_transform(
@@ -338,7 +338,7 @@ class zip_view<_Views...>::__iterator : public __zip_view_iterator_category_base
     return __x.__current_ <=> __y.__current_;
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(const __iterator& __i, difference_type __n)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(const __iterator& __i, difference_type __n)
     requires __zip_all_random_access<_Const, _Views...>
   {
     auto __r = __i;
@@ -346,13 +346,13 @@ class zip_view<_Views...>::__iterator : public __zip_view_iterator_category_base
     return __r;
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(difference_type __n, const __iterator& __i)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(difference_type __n, const __iterator& __i)
     requires __zip_all_random_access<_Const, _Views...>
   {
     return __i + __n;
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator-(const __iterator& __i, difference_type __n)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator-(const __iterator& __i, difference_type __n)
     requires __zip_all_random_access<_Const, _Views...>
   {
     auto __r = __i;
@@ -360,7 +360,8 @@ class zip_view<_Views...>::__iterator : public __zip_view_iterator_category_base
     return __r;
   }
 
-  _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<__maybe_const<_Const, _Views>>, iterator_t<__maybe_const<_Const, _Views>>> &&
              ...)
   {
@@ -374,7 +375,7 @@ class zip_view<_Views...>::__iterator : public __zip_view_iterator_category_base
         __diffs);
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr auto iter_move(const __iterator& __i) noexcept(
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr auto iter_move(const __iterator& __i) noexcept(
       (noexcept(ranges::iter_move(std::declval<const iterator_t<__maybe_const<_Const, _Views>>&>())) && ...) &&
       (is_nothrow_move_constructible_v<range_rvalue_reference_t<__maybe_const<_Const, _Views>>> && ...)) {
     return std::__tuple_transform(ranges::iter_move, __i.__current_);
@@ -426,6 +427,7 @@ class zip_view<_Views...>::__sentinel {
     requires(
         sized_sentinel_for<sentinel_t<__maybe_const<_Const, _Views>>, iterator_t<__maybe_const<_OtherConst, _Views>>> &&
         ...)
+  [[nodiscard]]
   _LIBCPP_HIDE_FROM_ABI friend constexpr common_type_t<range_difference_t<__maybe_const<_OtherConst, _Views>>...>
   operator-(const __iterator<_OtherConst>& __x, const __sentinel& __y) {
     const auto __diffs = ranges::__tuple_zip_transform(minus<>(), __iter_current(__x), __y.__end_);
@@ -443,6 +445,7 @@ class zip_view<_Views...>::__sentinel {
     requires(
         sized_sentinel_for<sentinel_t<__maybe_const<_Const, _Views>>, iterator_t<__maybe_const<_OtherConst, _Views>>> &&
         ...)
+  [[nodiscard]]
   _LIBCPP_HIDE_FROM_ABI friend constexpr common_type_t<range_difference_t<__maybe_const<_OtherConst, _Views>>...>
   operator-(const __sentinel& __y, const __iterator<_OtherConst>& __x) {
     return -(__x - __y);
@@ -456,10 +459,10 @@ namespace views {
 namespace __zip {
 
 struct __fn {
-  _LIBCPP_HIDE_FROM_ABI static constexpr auto operator()() noexcept { return empty_view<tuple<>>{}; }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto operator()() noexcept { return empty_view<tuple<>>{}; }
 
   template <class... _Ranges>
-  _LIBCPP_HIDE_FROM_ABI static constexpr auto
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto
   operator()(_Ranges&&... __rs) noexcept(noexcept(zip_view<all_t<_Ranges&&>...>(std::forward<_Ranges>(__rs)...)))
       -> decltype(zip_view<all_t<_Ranges&&>...>(std::forward<_Ranges>(__rs)...)) {
     return zip_view<all_t<_Ranges>...>(std::forward<_Ranges>(__rs)...);
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.zip/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.zip/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..59a1518a006ef
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.zip/nodiscard.verify.cpp
@@ -0,0 +1,92 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+// Check that functions are marked [[nodiscard]]
+
+#include <ranges>
+#include <vector>
+#include <utility>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+
+void test() {
+  int arr[]{94, 82, 49};
+  std::vector<int> range;
+
+  std::ranges::zip_view view{range, arr};
+
+  // [range.zip.view]
+  {
+    // 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();
+
+    // 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();
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    view.size();
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::as_const(view).size();
+  }
+
+  // [range.zip.iterator]
+
+  {
+    auto it = view.begin();
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    *std::as_const(it);
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::as_const(it)[0];
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    it + 1;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    1 + it;
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    it - 1;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    it - it;
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    iter_move(it);
+  }
+
+  // [range.zip.sentinel]
+
+  {
+    auto it = view.begin();
+    auto st = view.end();
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::as_const(it) - st;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    st - std::as_const(it);
+  }
+
+  // [range.zip.overview]
+
+  {
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::views::zip();
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::views::zip(arr, arr);
+  }
+}



More information about the libcxx-commits mailing list