[libcxx-commits] [libcxx] 4b7bad9 - [libc++] Implement D2351R0 "Mark all library static cast wrappers as [[nodiscard]]"

Arthur O'Dwyer via libcxx-commits libcxx-commits at lists.llvm.org
Mon Apr 12 09:29:45 PDT 2021


Author: Arthur O'Dwyer
Date: 2021-04-12T12:29:15-04:00
New Revision: 4b7bad9eaea2233521a94f6b096aaa88dc584e23

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

LOG: [libc++] Implement D2351R0 "Mark all library static cast wrappers as [[nodiscard]]"

These [[nodiscard]] annotations are added as a conforming extension;
it's unclear whether the paper will actually be adopted and make them
mandatory, but they do seem like good ideas regardless.

https://isocpp.org/files/papers/D2351R0.pdf

This patch implements the paper's effect on:
- std::to_integer, std::to_underlying
- std::forward, std::move, std::move_if_noexcept
- std::as_const
- std::identity

The paper also affects (but libc++ does not yet have an implementation of):
- std::bit_cast

Differential Revision: https://reviews.llvm.org/D99895

Added: 
    

Modified: 
    libcxx/docs/UsingLibcxx.rst
    libcxx/include/cstddef
    libcxx/include/functional
    libcxx/include/type_traits
    libcxx/include/utility
    libcxx/test/libcxx/diagnostics/nodiscard_extensions.pass.cpp
    libcxx/test/libcxx/diagnostics/nodiscard_extensions.verify.cpp
    libcxx/test/std/utilities/utility/forward/forward.fail.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/docs/UsingLibcxx.rst b/libcxx/docs/UsingLibcxx.rst
index 6bf67ce22db5..0e6d92bbbcd7 100644
--- a/libcxx/docs/UsingLibcxx.rst
+++ b/libcxx/docs/UsingLibcxx.rst
@@ -287,15 +287,15 @@ applications of ``[[nodiscard]]`` takes two forms:
 1. Backporting ``[[nodiscard]]`` to entities declared as such by the
    standard in newer dialects, but not in the present one.
 
-2. Extended applications of ``[[nodiscard]]``, at the libraries discretion,
+2. Extended applications of ``[[nodiscard]]``, at the library's discretion,
    applied to entities never declared as such by the standard.
 
 Users may also opt-out of additional applications ``[[nodiscard]]`` using
 additional macros.
 
 Applications of the first form, which backport ``[[nodiscard]]`` from a newer
-dialect may be disabled using macros specific to the dialect it was added. For
-example ``_LIBCPP_DISABLE_NODISCARD_AFTER_CXX17``.
+dialect, may be disabled using macros specific to the dialect in which it was
+added. For example, ``_LIBCPP_DISABLE_NODISCARD_AFTER_CXX17``.
 
 Applications of the second form, which are pure extensions, may be disabled
 by defining ``_LIBCPP_DISABLE_NODISCARD_EXT``.
@@ -346,3 +346,10 @@ which no dialect declares as such (See the second form described above).
 * ``unique``
 * ``upper_bound``
 * ``lock_guard``'s constructors
+* ``as_const``
+* ``forward``
+* ``move``
+* ``move_if_noexcept``
+* ``identity::operator()``
+* ``to_integer``
+* ``to_underlying``

diff  --git a/libcxx/include/cstddef b/libcxx/include/cstddef
index 2a0bfeb6e15f..ee86d6d2f6b4 100644
--- a/libcxx/include/cstddef
+++ b/libcxx/include/cstddef
@@ -152,7 +152,7 @@ template <class _Integer>
   { return static_cast<byte>(static_cast<unsigned char>(static_cast<unsigned int>(__lhs) >> __shift)); }
 
 template <class _Integer, class = _EnableByteOverload<_Integer> >
-  constexpr _Integer
+  _LIBCPP_NODISCARD_EXT constexpr _Integer
   to_integer(byte __b) noexcept { return static_cast<_Integer>(__b); }
 }
 

diff  --git a/libcxx/include/functional b/libcxx/include/functional
index 47449b7c8e22..e80f7aedd921 100644
--- a/libcxx/include/functional
+++ b/libcxx/include/functional
@@ -3213,7 +3213,7 @@ using unwrap_ref_decay_t = typename unwrap_ref_decay<_Tp>::type;
 // [func.identity]
 struct identity {
     template<class _Tp>
-    constexpr _Tp&& operator()(_Tp&& __t) const noexcept
+    _LIBCPP_NODISCARD_EXT constexpr _Tp&& operator()(_Tp&& __t) const noexcept
     {
         return _VSTD::forward<_Tp>(__t);
     }

diff  --git a/libcxx/include/type_traits b/libcxx/include/type_traits
index 761e883422bd..43a04cbf049a 100644
--- a/libcxx/include/type_traits
+++ b/libcxx/include/type_traits
@@ -2784,7 +2784,7 @@ _LIBCPP_INLINE_VAR _LIBCPP_CONSTEXPR bool is_destructible_v
 // move
 
 template <class _Tp>
-inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR
+_LIBCPP_NODISCARD_EXT inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR
 typename remove_reference<_Tp>::type&&
 move(_Tp&& __t) _NOEXCEPT
 {
@@ -2793,7 +2793,7 @@ move(_Tp&& __t) _NOEXCEPT
 }
 
 template <class _Tp>
-inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR
+_LIBCPP_NODISCARD_EXT inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR
 _Tp&&
 forward(typename remove_reference<_Tp>::type& __t) _NOEXCEPT
 {
@@ -2801,12 +2801,12 @@ forward(typename remove_reference<_Tp>::type& __t) _NOEXCEPT
 }
 
 template <class _Tp>
-inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR
+_LIBCPP_NODISCARD_EXT inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR
 _Tp&&
 forward(typename remove_reference<_Tp>::type&& __t) _NOEXCEPT
 {
     static_assert(!is_lvalue_reference<_Tp>::value,
-                  "can not forward an rvalue as an lvalue");
+                  "cannot forward an rvalue as an lvalue");
     return static_cast<_Tp&&>(__t);
 }
 

diff  --git a/libcxx/include/utility b/libcxx/include/utility
index bfde01c587e2..925b9521a5cb 100644
--- a/libcxx/include/utility
+++ b/libcxx/include/utility
@@ -260,7 +260,7 @@ operator>=(const _Tp& __x, const _Tp& __y)
 // move_if_noexcept
 
 template <class _Tp>
-inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX11
+_LIBCPP_NODISCARD_EXT inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX11
 #ifndef _LIBCPP_CXX03_LANG
 typename conditional
 <
@@ -277,8 +277,11 @@ move_if_noexcept(_Tp& __x) _NOEXCEPT
 }
 
 #if _LIBCPP_STD_VER > 14
-template <class _Tp> constexpr add_const_t<_Tp>& as_const(_Tp& __t) noexcept { return __t; }
-template <class _Tp>                        void as_const(const _Tp&&) = delete;
+template <class _Tp>
+_LIBCPP_NODISCARD_EXT constexpr add_const_t<_Tp>& as_const(_Tp& __t) noexcept { return __t; }
+
+template <class _Tp>
+void as_const(const _Tp&&) = delete;
 #endif
 
 struct _LIBCPP_TEMPLATE_VIS piecewise_construct_t { explicit piecewise_construct_t() = default; };
@@ -1636,7 +1639,7 @@ __to_underlying(_Tp __val) noexcept {
 
 #if _LIBCPP_STD_VER > 20
 template <class _Tp>
-_LIBCPP_INLINE_VISIBILITY constexpr underlying_type_t<_Tp>
+_LIBCPP_NODISCARD_EXT _LIBCPP_INLINE_VISIBILITY constexpr underlying_type_t<_Tp>
 to_underlying(_Tp __val) noexcept {
   return _VSTD::__to_underlying(__val);
 }

diff  --git a/libcxx/test/libcxx/diagnostics/nodiscard_extensions.pass.cpp b/libcxx/test/libcxx/diagnostics/nodiscard_extensions.pass.cpp
index cfb7054d3e2d..1b34e37092e8 100644
--- a/libcxx/test/libcxx/diagnostics/nodiscard_extensions.pass.cpp
+++ b/libcxx/test/libcxx/diagnostics/nodiscard_extensions.pass.cpp
@@ -23,9 +23,11 @@
 // be listed in `UsingLibcxx.rst` in the documentation for the extension.
 
 #include <algorithm>
-#include <functional>
+#include <cstddef> // to_integer
+#include <functional> // identity
 #include <iterator>
 #include <memory>
+#include <utility> // to_underlying
 
 #include "test_macros.h"
 
@@ -33,7 +35,7 @@ struct P {
   bool operator()(int) const { return false; }
 };
 
-int main(int, char**) {
+void test_algorithms() {
   int arr[1] = { 1 };
 
   std::adjacent_find(std::begin(arr), std::end(arr));
@@ -144,6 +146,51 @@ int main(int, char**) {
   std::unique(std::begin(arr), std::end(arr), std::greater<int>());
   std::upper_bound(std::begin(arr), std::end(arr), 1);
   std::upper_bound(std::begin(arr), std::end(arr), 1, std::greater<int>());
+}
+
+template<class LV, class RV>
+void test_template_cast_wrappers(LV&& lv, RV&& rv) {
+  std::forward<LV>(lv);
+  std::forward<RV>(rv);
+  std::move(lv);
+  std::move(rv);
+  std::move_if_noexcept(lv);
+  std::move_if_noexcept(rv);
+
+#if TEST_STD_VER >= 17
+  std::as_const(lv);
+  std::as_const(rv);
+#endif
+
+#if TEST_STD_VER >= 20
+  std::identity()(lv);
+  std::identity()(rv);
+#endif
+}
+
+void test_nontemplate_cast_wrappers()
+{
+#if TEST_STD_VER >= 17
+  std::byte b{42};
+  std::to_integer<int>(b);
+#endif
+
+#if TEST_STD_VER >= 20
+  // std::bit_cast<unsigned int>(42);
+#endif
+
+#if TEST_STD_VER > 20
+  enum E { Apple, Orange } e = Apple;
+  std::to_underlying(e);
+#endif
+}
+
+int main(int, char**) {
+  test_algorithms();
+
+  int i = 42;
+  test_template_cast_wrappers(i, std::move(i));
+  test_nontemplate_cast_wrappers();
 
   return 0;
 }

diff  --git a/libcxx/test/libcxx/diagnostics/nodiscard_extensions.verify.cpp b/libcxx/test/libcxx/diagnostics/nodiscard_extensions.verify.cpp
index b04e3e9865a0..5186746339c2 100644
--- a/libcxx/test/libcxx/diagnostics/nodiscard_extensions.verify.cpp
+++ b/libcxx/test/libcxx/diagnostics/nodiscard_extensions.verify.cpp
@@ -23,9 +23,11 @@
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_NODISCARD
 
 #include <algorithm>
-#include <functional>
+#include <cstddef> // to_integer
+#include <functional> // identity
 #include <iterator>
 #include <memory>
+#include <utility> // to_underlying
 
 #include "test_macros.h"
 
@@ -33,7 +35,7 @@ struct P {
   bool operator()(int) const { return false; }
 };
 
-int main(int, char**) {
+void test_algorithms() {
   int arr[1] = { 1 };
 
   // expected-warning-re at +1 {{ignoring return value of function declared with {{'nodiscard'|warn_unused_result}} attribute}}
@@ -291,6 +293,63 @@ int main(int, char**) {
 
   // expected-warning-re at +1 {{ignoring return value of function declared with {{'nodiscard'|warn_unused_result}} attribute}}
   std::upper_bound(std::begin(arr), std::end(arr), 1, std::greater<int>());
+}
+
+template<class LV, class RV>
+void test_template_cast_wrappers(LV&& lv, RV&& rv) {
+  // expected-warning-re at +1 {{ignoring return value of function declared with {{'nodiscard'|warn_unused_result}} attribute}}
+  std::forward<LV>(lv);
+  // expected-warning-re at +1 {{ignoring return value of function declared with {{'nodiscard'|warn_unused_result}} attribute}}
+  std::forward<RV>(rv);
+  // expected-warning-re at +1 {{ignoring return value of function declared with {{'nodiscard'|warn_unused_result}} attribute}}
+  std::move(lv);
+  // expected-warning-re at +1 {{ignoring return value of function declared with {{'nodiscard'|warn_unused_result}} attribute}}
+  std::move(rv);
+  // expected-warning-re at +1 {{ignoring return value of function declared with {{'nodiscard'|warn_unused_result}} attribute}}
+  std::move_if_noexcept(lv);
+  // expected-warning-re at +1 {{ignoring return value of function declared with {{'nodiscard'|warn_unused_result}} attribute}}
+  std::move_if_noexcept(rv);
+
+#if TEST_STD_VER >= 17
+  // expected-warning-re at +1 {{ignoring return value of function declared with {{'nodiscard'|warn_unused_result}} attribute}}
+  std::as_const(lv);
+  // expected-warning-re at +1 {{ignoring return value of function declared with {{'nodiscard'|warn_unused_result}} attribute}}
+  std::as_const(rv);
+#endif
+
+#if TEST_STD_VER >= 20
+  // expected-warning-re at +1 {{ignoring return value of function declared with {{'nodiscard'|warn_unused_result}} attribute}}
+  std::identity()(lv);
+  // expected-warning-re at +1 {{ignoring return value of function declared with {{'nodiscard'|warn_unused_result}} attribute}}
+  std::identity()(rv);
+#endif
+}
+
+void test_nontemplate_cast_wrappers()
+{
+#if TEST_STD_VER >= 17
+  std::byte b{42};
+  // expected-warning-re at +1 {{ignoring return value of function declared with {{'nodiscard'|warn_unused_result}} attribute}}
+  std::to_integer<int>(b);
+#endif
+
+#if TEST_STD_VER >= 20
+  // std::bit_cast<unsigned int>(42);
+#endif
+
+#if TEST_STD_VER > 20
+  enum E { Apple, Orange } e = Apple;
+  // expected-warning-re at +1 {{ignoring return value of function declared with {{'nodiscard'|warn_unused_result}} attribute}}
+  std::to_underlying(e);
+#endif
+}
+
+int main(int, char**) {
+  test_algorithms();
+
+  int i = 42;
+  test_template_cast_wrappers(i, std::move(i));
+  test_nontemplate_cast_wrappers();
 
   return 0;
 }

diff  --git a/libcxx/test/std/utilities/utility/forward/forward.fail.cpp b/libcxx/test/std/utilities/utility/forward/forward.fail.cpp
index 7dcdf25c5386..90287dc88de1 100644
--- a/libcxx/test/std/utilities/utility/forward/forward.fail.cpp
+++ b/libcxx/test/std/utilities/utility/forward/forward.fail.cpp
@@ -23,7 +23,7 @@ int main(int, char**)
 {
     {
         std::forward<A&>(source());  // expected-note {{requested here}}
-        // expected-error-re at type_traits:* 1 {{static_assert failed{{.*}} "can not forward an rvalue as an lvalue"}}
+        // expected-error-re at type_traits:* 1 {{static_assert failed{{.*}} "cannot forward an rvalue as an lvalue"}}
     }
     {
         const A ca = A();


        


More information about the libcxx-commits mailing list