forked from OSchip/llvm-project
[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
This commit is contained in:
parent
d7eb797ea5
commit
4b7bad9eae
|
@ -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``
|
||||
|
|
|
@ -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); }
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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@+1 {{ignoring return value of function declared with {{'nodiscard'|warn_unused_result}} attribute}}
|
||||
|
@ -291,6 +293,63 @@ int main(int, char**) {
|
|||
|
||||
// expected-warning-re@+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@+1 {{ignoring return value of function declared with {{'nodiscard'|warn_unused_result}} attribute}}
|
||||
std::forward<LV>(lv);
|
||||
// expected-warning-re@+1 {{ignoring return value of function declared with {{'nodiscard'|warn_unused_result}} attribute}}
|
||||
std::forward<RV>(rv);
|
||||
// expected-warning-re@+1 {{ignoring return value of function declared with {{'nodiscard'|warn_unused_result}} attribute}}
|
||||
std::move(lv);
|
||||
// expected-warning-re@+1 {{ignoring return value of function declared with {{'nodiscard'|warn_unused_result}} attribute}}
|
||||
std::move(rv);
|
||||
// expected-warning-re@+1 {{ignoring return value of function declared with {{'nodiscard'|warn_unused_result}} attribute}}
|
||||
std::move_if_noexcept(lv);
|
||||
// expected-warning-re@+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@+1 {{ignoring return value of function declared with {{'nodiscard'|warn_unused_result}} attribute}}
|
||||
std::as_const(lv);
|
||||
// expected-warning-re@+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@+1 {{ignoring return value of function declared with {{'nodiscard'|warn_unused_result}} attribute}}
|
||||
std::identity()(lv);
|
||||
// expected-warning-re@+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@+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@+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;
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ int main(int, char**)
|
|||
{
|
||||
{
|
||||
std::forward<A&>(source()); // expected-note {{requested here}}
|
||||
// expected-error-re@type_traits:* 1 {{static_assert failed{{.*}} "can not forward an rvalue as an lvalue"}}
|
||||
// expected-error-re@type_traits:* 1 {{static_assert failed{{.*}} "cannot forward an rvalue as an lvalue"}}
|
||||
}
|
||||
{
|
||||
const A ca = A();
|
||||
|
|
Loading…
Reference in New Issue