Skip to content

Commit 277a9f6

Browse files
committed
[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
1 parent dc9c411 commit 277a9f6

File tree

5 files changed

+45
-10
lines changed

5 files changed

+45
-10
lines changed

libcxx/include/__config

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1055,6 +1055,12 @@ _LIBCPP_BEGIN_NAMESPACE_STD _LIBCPP_END_NAMESPACE_STD
10551055
# define _LIBCPP_FALLTHROUGH() ((void)0)
10561056
# endif
10571057

1058+
# if __has_cpp_attribute(_Clang::__lifetimebound__)
1059+
# define _LIBCPP_LIFETIMEBOUND [[_Clang::__lifetimebound__]]
1060+
# else
1061+
# define _LIBCPP_LIFETIMEBOUND
1062+
# endif
1063+
10581064
# if __has_attribute(__nodebug__)
10591065
# define _LIBCPP_NODEBUG __attribute__((__nodebug__))
10601066
# else

libcxx/include/__utility/forward.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,14 @@
2121
_LIBCPP_BEGIN_NAMESPACE_STD
2222

2323
template <class _Tp>
24-
_LIBCPP_NODISCARD_EXT inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR _Tp&&
25-
forward(__libcpp_remove_reference_t<_Tp>& __t) _NOEXCEPT {
24+
_LIBCPP_NODISCARD_EXT inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp&&
25+
forward(_LIBCPP_LIFETIMEBOUND __libcpp_remove_reference_t<_Tp>& __t) _NOEXCEPT {
2626
return static_cast<_Tp&&>(__t);
2727
}
2828

2929
template <class _Tp>
30-
_LIBCPP_NODISCARD_EXT inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR _Tp&&
31-
forward(__libcpp_remove_reference_t<_Tp>&& __t) _NOEXCEPT {
30+
_LIBCPP_NODISCARD_EXT inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp&&
31+
forward(_LIBCPP_LIFETIMEBOUND __libcpp_remove_reference_t<_Tp>&& __t) _NOEXCEPT {
3232
static_assert(!is_lvalue_reference<_Tp>::value, "cannot forward an rvalue as an lvalue");
3333
return static_cast<_Tp&&>(__t);
3434
}

libcxx/include/__utility/forward_like.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ template <class _Ap, class _Bp>
3434
using _ForwardLike = _OverrideRef<_Ap&&, _CopyConst<remove_reference_t<_Ap>, remove_reference_t<_Bp>>>;
3535

3636
template <class _Tp, class _Up>
37-
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto forward_like(_Up&& __ux) noexcept -> _ForwardLike<_Tp, _Up> {
37+
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto forward_like(_LIBCPP_LIFETIMEBOUND _Up&& __ux) noexcept
38+
-> _ForwardLike<_Tp, _Up> {
3839
return static_cast<_ForwardLike<_Tp, _Up>>(__ux);
3940
}
4041

libcxx/include/__utility/move.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@
2323
_LIBCPP_BEGIN_NAMESPACE_STD
2424

2525
template <class _Tp>
26-
_LIBCPP_NODISCARD_EXT inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR __libcpp_remove_reference_t<_Tp>&&
27-
move(_Tp&& __t) _NOEXCEPT {
26+
_LIBCPP_NODISCARD_EXT inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __libcpp_remove_reference_t<_Tp>&&
27+
move(_LIBCPP_LIFETIMEBOUND _Tp&& __t) _NOEXCEPT {
2828
typedef _LIBCPP_NODEBUG __libcpp_remove_reference_t<_Tp> _Up;
2929
return static_cast<_Up&&>(__t);
3030
}
@@ -34,9 +34,9 @@ using __move_if_noexcept_result_t =
3434
__conditional_t<!is_nothrow_move_constructible<_Tp>::value && is_copy_constructible<_Tp>::value, const _Tp&, _Tp&&>;
3535

3636
template <class _Tp>
37-
_LIBCPP_NODISCARD_EXT inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_SINCE_CXX14 __move_if_noexcept_result_t<_Tp>
38-
move_if_noexcept(_Tp& __x) _NOEXCEPT {
39-
return _VSTD::move(__x);
37+
_LIBCPP_NODISCARD_EXT inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 __move_if_noexcept_result_t<_Tp>
38+
move_if_noexcept(_LIBCPP_LIFETIMEBOUND _Tp& __x) _NOEXCEPT {
39+
return std::move(__x);
4040
}
4141

4242
_LIBCPP_END_NAMESPACE_STD
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
// UNSUPPORTED: c++03
10+
// ADDITIONAL_COMPILE_FLAGS: -Wno-pessimizing-move -Wno-unused-variable
11+
12+
#include <utility>
13+
14+
#include "test_macros.h"
15+
16+
struct S {
17+
const int& func() [[clang::lifetimebound]];
18+
};
19+
20+
void func() {
21+
auto&& v1 = std::move(int{}); // expected-warning {{temporary bound to local reference 'v1' will be destroyed at the end of the full-expression}}
22+
auto&& v2 = std::forward<int&&>(int{}); // expected-warning {{temporary bound to local reference 'v2' will be destroyed at the end of the full-expression}}
23+
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}}
24+
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}}
25+
#if TEST_STD_VER >= 23
26+
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}}
27+
#endif
28+
}

0 commit comments

Comments
 (0)