[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