Skip to content

Conversation

@H-G-Hristov
Copy link
Contributor

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

@github-actions
Copy link

github-actions bot commented Dec 12, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

@H-G-Hristov H-G-Hristov force-pushed the hgh/libcxx/nodiscard-to-tuple branch 3 times, most recently from 6ddf3dd to aba73cc Compare December 12, 2025 18:17
@H-G-Hristov
Copy link
Contributor Author

H-G-Hristov commented Dec 12, 2025

@H-G-Hristov H-G-Hristov force-pushed the hgh/libcxx/nodiscard-to-tuple branch 4 times, most recently from d53724d to afc0f86 Compare December 12, 2025 20:10
@H-G-Hristov H-G-Hristov marked this pull request as ready for review December 14, 2025 07:52
@H-G-Hristov H-G-Hristov requested a review from a team as a code owner December 14, 2025 07:52
@llvmbot llvmbot added the libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. label Dec 14, 2025
@llvmbot
Copy link
Member

llvmbot commented Dec 14, 2025

@llvm/pr-subscribers-libcxx

Author: Hristo Hristov (H-G-Hristov)

Changes

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


Full diff: https://github.com/llvm/llvm-project/pull/172008.diff

3 Files Affected:

  • (modified) libcxx/include/tuple (+19-14)
  • (added) libcxx/test/libcxx/utilities/tuple/nodiscard.verify.cpp (+64)
  • (modified) libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/recursion_depth.pass.cpp (+4-2)
diff --git a/libcxx/include/tuple b/libcxx/include/tuple
index 670b90fc7b3b9..e02e45f425f4f 100644
--- a/libcxx/include/tuple
+++ b/libcxx/include/tuple
@@ -1113,28 +1113,32 @@ swap(const tuple<_Tp...>& __lhs,
 // get
 
 template <size_t _Ip, class... _Tp>
-inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 typename tuple_element<_Ip, tuple<_Tp...> >::type&
+[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14
+typename tuple_element<_Ip, tuple<_Tp...> >::type&
 get(tuple<_Tp...>& __t) _NOEXCEPT {
   using type _LIBCPP_NODEBUG = typename tuple_element<_Ip, tuple<_Tp...> >::type;
   return static_cast<__tuple_leaf<_Ip, type>&>(__t.__base_).get();
 }
 
 template <size_t _Ip, class... _Tp>
-inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 const typename tuple_element<_Ip, tuple<_Tp...> >::type&
+[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI
+_LIBCPP_CONSTEXPR_SINCE_CXX14 const typename tuple_element<_Ip, tuple<_Tp...> >::type&
 get(const tuple<_Tp...>& __t) _NOEXCEPT {
   using type _LIBCPP_NODEBUG = typename tuple_element<_Ip, tuple<_Tp...> >::type;
   return static_cast<const __tuple_leaf<_Ip, type>&>(__t.__base_).get();
 }
 
 template <size_t _Ip, class... _Tp>
-inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 typename tuple_element<_Ip, tuple<_Tp...> >::type&&
+[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14
+typename tuple_element<_Ip, tuple<_Tp...> >::type&&
 get(tuple<_Tp...>&& __t) _NOEXCEPT {
   using type _LIBCPP_NODEBUG = typename tuple_element<_Ip, tuple<_Tp...> >::type;
   return static_cast<type&&>(static_cast<__tuple_leaf<_Ip, type>&&>(__t.__base_).get());
 }
 
 template <size_t _Ip, class... _Tp>
-inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 const typename tuple_element<_Ip, tuple<_Tp...> >::type&&
+[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI
+_LIBCPP_CONSTEXPR_SINCE_CXX14 const typename tuple_element<_Ip, tuple<_Tp...> >::type&&
 get(const tuple<_Tp...>&& __t) _NOEXCEPT {
   using type _LIBCPP_NODEBUG = typename tuple_element<_Ip, tuple<_Tp...> >::type;
   return static_cast<const type&&>(static_cast<const __tuple_leaf<_Ip, type>&&>(__t.__base_).get());
@@ -1143,22 +1147,22 @@ get(const tuple<_Tp...>&& __t) _NOEXCEPT {
 #    if _LIBCPP_STD_VER >= 14
 
 template <class _T1, class... _Args>
-inline _LIBCPP_HIDE_FROM_ABI constexpr _T1& get(tuple<_Args...>& __tup) noexcept {
+[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI constexpr _T1& get(tuple<_Args...>& __tup) noexcept {
   return std::get<__find_exactly_one_t<_T1, _Args...>::value>(__tup);
 }
 
 template <class _T1, class... _Args>
-inline _LIBCPP_HIDE_FROM_ABI constexpr _T1 const& get(tuple<_Args...> const& __tup) noexcept {
+[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI constexpr _T1 const& get(tuple<_Args...> const& __tup) noexcept {
   return std::get<__find_exactly_one_t<_T1, _Args...>::value>(__tup);
 }
 
 template <class _T1, class... _Args>
-inline _LIBCPP_HIDE_FROM_ABI constexpr _T1&& get(tuple<_Args...>&& __tup) noexcept {
+[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI constexpr _T1&& get(tuple<_Args...>&& __tup) noexcept {
   return std::get<__find_exactly_one_t<_T1, _Args...>::value>(std::move(__tup));
 }
 
 template <class _T1, class... _Args>
-inline _LIBCPP_HIDE_FROM_ABI constexpr _T1 const&& get(tuple<_Args...> const&& __tup) noexcept {
+[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI constexpr _T1 const&& get(tuple<_Args...> const&& __tup) noexcept {
   return std::get<__find_exactly_one_t<_T1, _Args...>::value>(std::move(__tup));
 }
 
@@ -1167,18 +1171,19 @@ inline _LIBCPP_HIDE_FROM_ABI constexpr _T1 const&& get(tuple<_Args...> const&& _
 // tie
 
 template <class... _Tp>
-inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 tuple<_Tp&...> tie(_Tp&... __t) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 tuple<_Tp&...> tie(_Tp&... __t) _NOEXCEPT {
   return tuple<_Tp&...>(__t...);
 }
 
 template <class... _Tp>
-inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 tuple<__unwrap_ref_decay_t<_Tp>...>
+[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 tuple<__unwrap_ref_decay_t<_Tp>...>
 make_tuple(_Tp&&... __t) {
   return tuple<__unwrap_ref_decay_t<_Tp>...>(std::forward<_Tp>(__t)...);
 }
 
 template <class... _Tp>
-inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 tuple<_Tp&&...> forward_as_tuple(_Tp&&... __t) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 tuple<_Tp&&...>
+forward_as_tuple(_Tp&&... __t) _NOEXCEPT {
   return tuple<_Tp&&...>(std::forward<_Tp>(__t)...);
 }
 
@@ -1300,7 +1305,7 @@ template <class... _Tuples>
 using __tuple_cat_return_t _LIBCPP_NODEBUG =
     typename __tuple_cat_return_impl<tuple<>, __remove_cvref_t<_Tuples>...>::type;
 
-inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 tuple<> tuple_cat() { return tuple<>(); }
+[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 tuple<> tuple_cat() { return tuple<>(); }
 
 template <class _Rp, class _Indices, class _Tuple0, class... _Tuples>
 struct __tuple_cat_return_ref_imp;
@@ -1368,7 +1373,7 @@ __tuple_cat_select_element_wise(_TupleSrc&& __src, __index_sequence<_Indices...>
 }
 
 template <class _Tuple0, class... _Tuples>
-inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 __tuple_cat_return_t<_Tuple0, _Tuples...>
+[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 __tuple_cat_return_t<_Tuple0, _Tuples...>
 tuple_cat(_Tuple0&& __t0, _Tuples&&... __tpls) {
   using _T0 _LIBCPP_NODEBUG          = __libcpp_remove_reference_t<_Tuple0>;
   using _TRet _LIBCPP_NODEBUG        = __tuple_cat_return_t<_Tuple0, _Tuples...>;
@@ -1435,7 +1440,7 @@ template <class _Tp, class _Tuple>
 template <class _Tp, class _Tuple, class = enable_if_t<__can_make_from_tuple<_Tp, _Tuple>>> // strengthen
 #endif // _LIBCPP_STD_VER >= 20
 
-inline _LIBCPP_HIDE_FROM_ABI constexpr _Tp make_from_tuple(_Tuple&& __t)
+[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI constexpr _Tp make_from_tuple(_Tuple&& __t)
   noexcept(noexcept(std::__make_from_tuple_impl<_Tp>(std::forward<_Tuple>(__t),
                     make_index_sequence<tuple_size_v<remove_reference_t<_Tuple>>>()))) {
 #if _LIBCPP_STD_VER >= 23
diff --git a/libcxx/test/libcxx/utilities/tuple/nodiscard.verify.cpp b/libcxx/test/libcxx/utilities/tuple/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..6ca7310bab771
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/tuple/nodiscard.verify.cpp
@@ -0,0 +1,64 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++11
+
+// Check that functions are marked [[nodiscard]]
+
+#include <tuple>
+
+#include "test_macros.h"
+
+void test() {
+  struct First {};
+  struct Second {};
+  struct Third {};
+
+  std::tuple<First, Second, Third> t;
+  const std::tuple<First, Second, Third> ct;
+
+  // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::get<0>(t);
+  // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::get<0>(ct);
+  // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::get<0>(std::move(t));
+  // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::get<0>(std::move(t));
+#if TEST_STD_VER >= 14
+  // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::get<Third>(t);
+  // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::get<Third>(ct);
+  // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::get<Third>(std::move(t));
+  // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::get<Third>(std::move(t));
+#endif
+
+  // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::tie(ct);
+
+  First e1;
+  Second e2;
+  Third e3;
+
+  // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::make_tuple(e1, e2, e3);
+
+  // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::forward_as_tuple(First{}, Second{}, Third{});
+
+  // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::tuple_cat(std::tuple<First>{}, std::tuple<Second, Third>{});
+
+#if TEST_STD_VER >= 17
+  // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::make_from_tuple<First>(std::tuple<First>{});
+#endif
+}
diff --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/recursion_depth.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/recursion_depth.pass.cpp
index 57d37079c9f0c..ed44344edd0ad 100644
--- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/recursion_depth.pass.cpp
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/recursion_depth.pass.cpp
@@ -22,8 +22,10 @@ constexpr void CreateTuple(std::index_sequence<I...>) {
   using LargeTuple = std::tuple<std::integral_constant<std::size_t, I>...>;
   using TargetTuple = std::tuple<decltype(I)...>;
   LargeTuple tuple(std::integral_constant<std::size_t, I>{}...);
-  assert(std::get<0>(tuple).value == 0);
-  assert(std::get<sizeof...(I)-1>(tuple).value == sizeof...(I)-1);
+  auto e1 = std::get<0>(tuple);
+  assert(e1.value == 0);
+  auto e2 = std::get<sizeof...(I) - 1>(tuple);
+  assert(e2.value == sizeof...(I) - 1);
 
   TargetTuple t1 = tuple;                                  // converting copy constructor from &
   TargetTuple t2 = static_cast<LargeTuple const&>(tuple);  // converting copy constructor from const&

`[[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/tuple
@H-G-Hristov H-G-Hristov force-pushed the hgh/libcxx/nodiscard-to-tuple branch from afc0f86 to 80c248b Compare December 15, 2025 07:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants