[libc++] Add [[clang::lifetimebound]] attribute to std::forward and friends

This allows clang to catch lifetime bugs through these functions.
As a drive-by, replace `_LIBCPP_INLINE_VISIBILITY` with `_LIBCPP_HIDE_FROM_ABI`.
Fixes #59900

Reviewed By: ldionne, #libc

Spies: rsmith, rnk, aaron.ballman, libcxx-commits

Differential Revision: https://reviews.llvm.org/D141321
This commit is contained in:
Nikolas Klauser 2023-01-09 22:27:06 +01:00
parent dc9c41125c
commit 277a9f6e5f
5 changed files with 45 additions and 10 deletions

View File

@ -1055,6 +1055,12 @@ _LIBCPP_BEGIN_NAMESPACE_STD _LIBCPP_END_NAMESPACE_STD
# define _LIBCPP_FALLTHROUGH() ((void)0)
# endif
# if __has_cpp_attribute(_Clang::__lifetimebound__)
# define _LIBCPP_LIFETIMEBOUND [[_Clang::__lifetimebound__]]
# else
# define _LIBCPP_LIFETIMEBOUND
# endif
# if __has_attribute(__nodebug__)
# define _LIBCPP_NODEBUG __attribute__((__nodebug__))
# else

View File

@ -21,14 +21,14 @@
_LIBCPP_BEGIN_NAMESPACE_STD
template <class _Tp>
_LIBCPP_NODISCARD_EXT inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR _Tp&&
forward(__libcpp_remove_reference_t<_Tp>& __t) _NOEXCEPT {
_LIBCPP_NODISCARD_EXT inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp&&
forward(_LIBCPP_LIFETIMEBOUND __libcpp_remove_reference_t<_Tp>& __t) _NOEXCEPT {
return static_cast<_Tp&&>(__t);
}
template <class _Tp>
_LIBCPP_NODISCARD_EXT inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR _Tp&&
forward(__libcpp_remove_reference_t<_Tp>&& __t) _NOEXCEPT {
_LIBCPP_NODISCARD_EXT inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp&&
forward(_LIBCPP_LIFETIMEBOUND __libcpp_remove_reference_t<_Tp>&& __t) _NOEXCEPT {
static_assert(!is_lvalue_reference<_Tp>::value, "cannot forward an rvalue as an lvalue");
return static_cast<_Tp&&>(__t);
}

View File

@ -34,7 +34,8 @@ template <class _Ap, class _Bp>
using _ForwardLike = _OverrideRef<_Ap&&, _CopyConst<remove_reference_t<_Ap>, remove_reference_t<_Bp>>>;
template <class _Tp, class _Up>
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto forward_like(_Up&& __ux) noexcept -> _ForwardLike<_Tp, _Up> {
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto forward_like(_LIBCPP_LIFETIMEBOUND _Up&& __ux) noexcept
-> _ForwardLike<_Tp, _Up> {
return static_cast<_ForwardLike<_Tp, _Up>>(__ux);
}

View File

@ -23,8 +23,8 @@
_LIBCPP_BEGIN_NAMESPACE_STD
template <class _Tp>
_LIBCPP_NODISCARD_EXT inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR __libcpp_remove_reference_t<_Tp>&&
move(_Tp&& __t) _NOEXCEPT {
_LIBCPP_NODISCARD_EXT inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __libcpp_remove_reference_t<_Tp>&&
move(_LIBCPP_LIFETIMEBOUND _Tp&& __t) _NOEXCEPT {
typedef _LIBCPP_NODEBUG __libcpp_remove_reference_t<_Tp> _Up;
return static_cast<_Up&&>(__t);
}
@ -34,9 +34,9 @@ using __move_if_noexcept_result_t =
__conditional_t<!is_nothrow_move_constructible<_Tp>::value && is_copy_constructible<_Tp>::value, const _Tp&, _Tp&&>;
template <class _Tp>
_LIBCPP_NODISCARD_EXT inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_SINCE_CXX14 __move_if_noexcept_result_t<_Tp>
move_if_noexcept(_Tp& __x) _NOEXCEPT {
return _VSTD::move(__x);
_LIBCPP_NODISCARD_EXT inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 __move_if_noexcept_result_t<_Tp>
move_if_noexcept(_LIBCPP_LIFETIMEBOUND _Tp& __x) _NOEXCEPT {
return std::move(__x);
}
_LIBCPP_END_NAMESPACE_STD

View File

@ -0,0 +1,28 @@
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03
// ADDITIONAL_COMPILE_FLAGS: -Wno-pessimizing-move -Wno-unused-variable
#include <utility>
#include "test_macros.h"
struct S {
const int& func() [[clang::lifetimebound]];
};
void func() {
auto&& v1 = std::move(int{}); // expected-warning {{temporary bound to local reference 'v1' will be destroyed at the end of the full-expression}}
auto&& v2 = std::forward<int&&>(int{}); // expected-warning {{temporary bound to local reference 'v2' will be destroyed at the end of the full-expression}}
auto&& v3 = std::forward<const int&>(S{}.func()); // expected-warning {{temporary bound to local reference 'v3' will be destroyed at the end of the full-expression}}
auto&& v4 = std::move_if_noexcept<const int&>(S{}.func()); // expected-warning {{temporary bound to local reference 'v4' will be destroyed at the end of the full-expression}}
#if TEST_STD_VER >= 23
auto&& v5 = std::forward_like<int&&>(int{}); // expected-warning {{temporary bound to local reference 'v5' will be destroyed at the end of the full-expression}}
#endif
}