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

via libcxx-commits libcxx-commits at lists.llvm.org
Tue Jul 7 09:01:04 PDT 2026


Author: Hristo Hristov
Date: 2026-07-07T19:00:58+03:00
New Revision: 1045d1ebca559d7e6dee49b002142e52bb61b49e

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

LOG: [libc++][ranges] Applied `[[nodiscard]]` to `zip_view` (#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/range.zip

Towards #172124

Added: 
    libcxx/test/libcxx/ranges/range.adaptors/range.zip/nodiscard.verify.cpp
    libcxx/test/libcxx/ranges/range.adaptors/range_adaptor_types.h

Modified: 
    libcxx/include/__ranges/zip_view.h
    libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp

Removed: 
    


################################################################################
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[](
diff erence_type __n) const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator[](
diff erence_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, 
diff erence_type __n)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(const __iterator& __i, 
diff erence_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+(
diff erence_type __n, const __iterator& __i)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(
diff erence_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, 
diff erence_type __n)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator-(const __iterator& __i, 
diff erence_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 
diff erence_type operator-(const __iterator& __x, const __iterator& __y)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr 
diff erence_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
         __
diff s);
   }
 
-  _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_
diff erence_t<__maybe_const<_OtherConst, _Views>>...>
   operator-(const __iterator<_OtherConst>& __x, const __sentinel& __y) {
     const auto __
diff s = 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_
diff erence_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.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp
index e681d338dc786..0c4e60701e1eb 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp
@@ -14,6 +14,7 @@
 #include <utility>
 #include <vector>
 
+#include "../range_adaptor_types.h"
 #include "test_iterators.h"
 
 struct CommonView : std::ranges::view_interface<CommonView> {
@@ -28,26 +29,6 @@ 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>>);
 
-struct ForwardSizedNonCommon {
-  int* buffer_      = nullptr;
-  std::size_t size_ = 0;
-
-  template <std::size_t N>
-  constexpr ForwardSizedNonCommon(int (&b)[N]) : buffer_(b), size_(N) {}
-
-  constexpr ForwardSizedNonCommon(int* b, std::size_t s) : buffer_(b), size_(s) {}
-
-  using iterator = forward_sized_iterator<int*>;
-  using sentinel = sized_sentinel<iterator>;
-
-  constexpr iterator begin() const { return iterator(buffer_); }
-  constexpr sentinel end() const { return sentinel(iterator(buffer_ + size_)); }
-};
-static_assert(std::ranges::forward_range<ForwardSizedNonCommon>);
-static_assert(std::ranges::sized_range<ForwardSizedNonCommon>);
-static_assert(!std::ranges::common_range<ForwardSizedNonCommon>);
-static_assert(!std::ranges::random_access_range<ForwardSizedNonCommon>);
-
 template <class... Args>
 struct Invocable {
   int operator()(Args...) const { return 5; }

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..0667757b07658
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.zip/nodiscard.verify.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+// <ranges>
+
+// Check that functions are marked [[nodiscard]]
+
+#include <ranges>
+#include <vector>
+#include <utility>
+
+#include "../range_adaptor_types.h"
+#include "test_macros.h"
+#include "test_iterators.h"
+
+void test() {
+  int arr[]{94, 82, 49};
+  std::vector<int> range;
+
+  std::ranges::zip_view zv{range, arr};
+
+  // [range.zip.view]
+  {
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    zv.begin();
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::as_const(zv).begin();
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    zv.end();
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::as_const(zv).end();
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    zv.size();
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::as_const(zv).size();
+  }
+
+  // [range.zip.iterator]
+
+  {
+    auto it  = zv.begin();
+    auto cIt = std::as_const(zv).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}}
+    cIt[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]
+
+  {
+    std::ranges::zip_view nonCommonZv{ForwardSizedNonCommon(arr)};
+    static_assert(!std::ranges::common_range<decltype(nonCommonZv)>);
+    static_assert(!std::same_as<decltype(nonCommonZv.end()), decltype(nonCommonZv.begin())>);
+    auto it  = nonCommonZv.begin();
+    auto cIt = std::as_const(nonCommonZv).begin();
+    auto st  = nonCommonZv.end();
+    auto cSt = std::as_const(nonCommonZv).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}}
+    it - cSt;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    cSt - it;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    cIt - st;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    st - cIt;
+  }
+
+  // [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);
+  }
+}

diff  --git a/libcxx/test/libcxx/ranges/range.adaptors/range_adaptor_types.h b/libcxx/test/libcxx/ranges/range.adaptors/range_adaptor_types.h
new file mode 100644
index 0000000000000..cea4cb0dfddd5
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range_adaptor_types.h
@@ -0,0 +1,42 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_ADAPTOR_TYPES_H
+#define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_ADAPTOR_TYPES_H
+
+#include <cstdint>
+#include <ranges>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+
+#if TEST_STD_VER <= 20
+#  error "range.adaptor/types.h" can only be included in builds supporting C++20
+#endif // TEST_STD_VER <= 20
+
+struct ForwardSizedNonCommon {
+  int* buffer_      = nullptr;
+  std::size_t size_ = 0;
+
+  template <std::size_t N>
+  constexpr ForwardSizedNonCommon(int (&b)[N]) : buffer_(b), size_(N) {}
+
+  constexpr ForwardSizedNonCommon(int* b, std::size_t s) : buffer_(b), size_(s) {}
+
+  using iterator = forward_sized_iterator<int*>;
+  using sentinel = sized_sentinel<iterator>;
+
+  constexpr iterator begin() const { return iterator(buffer_); }
+  constexpr sentinel end() const { return sentinel(iterator(buffer_ + size_)); }
+};
+static_assert(std::ranges::forward_range<ForwardSizedNonCommon>);
+static_assert(std::ranges::sized_range<ForwardSizedNonCommon>);
+static_assert(!std::ranges::common_range<ForwardSizedNonCommon>);
+static_assert(!std::ranges::random_access_range<ForwardSizedNonCommon>);
+
+#endif //  TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_ADAPTOR_TYPES_H


        


More information about the libcxx-commits mailing list