From 277a9f6e5f85bfa1c58ca21c26a5a74326f002ff Mon Sep 17 00:00:00 2001 From: Nikolas Klauser Date: Mon, 9 Jan 2023 22:27:06 +0100 Subject: [PATCH] [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 --- libcxx/include/__config | 6 +++++ libcxx/include/__utility/forward.h | 8 +++---- libcxx/include/__utility/forward_like.h | 3 ++- libcxx/include/__utility/move.h | 10 ++++---- .../utility/forward/lifetimebound.verify.cpp | 28 ++++++++++++++++++++++ 5 files changed, 45 insertions(+), 10 deletions(-) create mode 100644 libcxx/test/libcxx/utilities/utility/forward/lifetimebound.verify.cpp diff --git a/libcxx/include/__config b/libcxx/include/__config index 505ccf9..a5d54d4 100644 --- a/libcxx/include/__config +++ b/libcxx/include/__config @@ -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 diff --git a/libcxx/include/__utility/forward.h b/libcxx/include/__utility/forward.h index 4e254e0..010f236 100644 --- a/libcxx/include/__utility/forward.h +++ b/libcxx/include/__utility/forward.h @@ -21,14 +21,14 @@ _LIBCPP_BEGIN_NAMESPACE_STD template -_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 -_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); } diff --git a/libcxx/include/__utility/forward_like.h b/libcxx/include/__utility/forward_like.h index 1446964..7bb0d7d 100644 --- a/libcxx/include/__utility/forward_like.h +++ b/libcxx/include/__utility/forward_like.h @@ -34,7 +34,8 @@ template using _ForwardLike = _OverrideRef<_Ap&&, _CopyConst, remove_reference_t<_Bp>>>; template -[[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); } diff --git a/libcxx/include/__utility/move.h b/libcxx/include/__utility/move.h index 2ffcba2..4859c39 100644 --- a/libcxx/include/__utility/move.h +++ b/libcxx/include/__utility/move.h @@ -23,8 +23,8 @@ _LIBCPP_BEGIN_NAMESPACE_STD template -_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::value && is_copy_constructible<_Tp>::value, const _Tp&, _Tp&&>; template -_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 diff --git a/libcxx/test/libcxx/utilities/utility/forward/lifetimebound.verify.cpp b/libcxx/test/libcxx/utilities/utility/forward/lifetimebound.verify.cpp new file mode 100644 index 0000000..4381c3a --- /dev/null +++ b/libcxx/test/libcxx/utilities/utility/forward/lifetimebound.verify.cpp @@ -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 + +#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{}); // expected-warning {{temporary bound to local reference 'v2' will be destroyed at the end of the full-expression}} + auto&& v3 = std::forward(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(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{}); // expected-warning {{temporary bound to local reference 'v5' will be destroyed at the end of the full-expression}} +#endif +} -- 2.7.4