diff --git a/libcxx/include/__config b/libcxx/include/__config --- a/libcxx/include/__config +++ b/libcxx/include/__config @@ -1055,6 +1055,12 @@ # 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 --- 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 --- a/libcxx/include/__utility/forward_like.h +++ b/libcxx/include/__utility/forward_like.h @@ -34,7 +34,8 @@ 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 --- 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 @@ __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 --- /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 +}