alexander-shaposhnikov created this revision. alexander-shaposhnikov added reviewers: rsmith, aaron.ballman, dblaikie. alexander-shaposhnikov created this object with visibility "All Users". Herald added a project: All. alexander-shaposhnikov requested review of this revision. Herald added a project: clang. Herald added a subscriber: cfe-commits.
Add lifetimebound attribute to std::move/std::forward. This diff addresses https://github.com/llvm/llvm-project/issues/59900 Test plan: ninja check-clang check-clang-tools check-llvm Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D141744 Files: clang/lib/Sema/SemaDecl.cpp clang/test/SemaCXX/attr-lifetimebound.cpp clang/test/SemaCXX/builtin-std-move.cpp Index: clang/test/SemaCXX/builtin-std-move.cpp =================================================================== --- clang/test/SemaCXX/builtin-std-move.cpp +++ clang/test/SemaCXX/builtin-std-move.cpp @@ -85,7 +85,7 @@ A &forward_rval_as_lval() { std::forward<A&&>(A()); // expected-warning {{const attribute}} - return std::forward<A&>(A()); // expected-note {{instantiation of}} + return std::forward<A&>(A()); // expected-note {{instantiation of}} expected-warning {{returning reference}} } struct B {}; Index: clang/test/SemaCXX/attr-lifetimebound.cpp =================================================================== --- clang/test/SemaCXX/attr-lifetimebound.cpp +++ clang/test/SemaCXX/attr-lifetimebound.cpp @@ -113,3 +113,55 @@ std::map<std::string, std::string> m; const std::string &v = findOrDefault(m, "foo"s, "bar"s); // expected-warning {{temporary bound to local reference 'v'}} } + +// definitions for std::move, std::forward et al. +namespace std { +inline namespace foo { + +template <class T> struct remove_reference { typedef T type; }; +template <class T> struct remove_reference<T&> { typedef T type; }; +template <class T> struct remove_reference<T&&> { typedef T type; }; + +template <class T> constexpr typename remove_reference<T>::type &&move(T &&t) { + return static_cast<typename remove_reference<T>::type>(t); +} + +template <class T> constexpr T&& forward(typename remove_reference<T>::type& t) { + return static_cast<T&&>(t); +} + +template <class T> constexpr T&& forward(typename remove_reference<T>::type&& t) { + return static_cast<T&&>(t); +} + +template<class T> constexpr const T &as_const(T &x) { + return x; +} + +template<class T, bool RValueRef> struct PickRef { using type = typename remove_reference<T>::type&; }; +template<class T> struct PickRef<T, true> { using type = typename remove_reference<T>::type&&; }; + +template<class T> auto move_if_noexcept(T &t) -> typename PickRef<T, noexcept(T(static_cast<T&&>(t)))>::type { + return static_cast<typename PickRef<T, noexcept(T(static_cast<T&&>(t)))>::type>(t); +} + +} // inline namespace foo +} // namespace std + +namespace move_forward_as_const_examples { + struct S { + S &self() [[clang::lifetimebound]] { return *this; } + }; + + S &&Move = std::move(S{}); // expected-warning {{temporary bound to local reference 'Move' will be destroyed at the end of the full-expression}} + S MoveOk = std::move(S{}); + + S &&Forward = std::forward<S&&>(S{}); // expected-warning {{temporary bound to local reference 'Forward' will be destroyed at the end of the full-expression}} + S ForwardOk = std::forward<S&&>(S{}); + + const S &Const = std::as_const(S{}.self()); // expected-warning {{temporary bound to local reference 'Const' will be destroyed at the end of the full-expression}} + const S ConstOk = std::as_const(S{}.self()); + + S &&MoveIfNoExcept = std::move_if_noexcept(S{}.self()); // expected-warning {{temporary bound to local reference 'MoveIfNoExcept' will be destroyed at the end of the full-expression}} + S MoveIfNoExceptOk = std::move_if_noexcept(S{}.self()); +} // namespace move_forward_as_const_examples Index: clang/lib/Sema/SemaDecl.cpp =================================================================== --- clang/lib/Sema/SemaDecl.cpp +++ clang/lib/Sema/SemaDecl.cpp @@ -16180,6 +16180,21 @@ default: break; } + + // Add lifetime attribute to std::move, std::fowrard et al. + switch (BuiltinID) { + case Builtin::BIas_const: + case Builtin::BIforward: + case Builtin::BImove: + case Builtin::BImove_if_noexcept: + if (ParmVarDecl *P = FD->getParamDecl(0u); + !P->hasAttr<LifetimeBoundAttr>()) + P->addAttr( + LifetimeBoundAttr::CreateImplicit(Context, FD->getLocation())); + break; + default: + break; + } } AddKnownFunctionAttributesForReplaceableGlobalAllocationFunction(FD);
Index: clang/test/SemaCXX/builtin-std-move.cpp =================================================================== --- clang/test/SemaCXX/builtin-std-move.cpp +++ clang/test/SemaCXX/builtin-std-move.cpp @@ -85,7 +85,7 @@ A &forward_rval_as_lval() { std::forward<A&&>(A()); // expected-warning {{const attribute}} - return std::forward<A&>(A()); // expected-note {{instantiation of}} + return std::forward<A&>(A()); // expected-note {{instantiation of}} expected-warning {{returning reference}} } struct B {}; Index: clang/test/SemaCXX/attr-lifetimebound.cpp =================================================================== --- clang/test/SemaCXX/attr-lifetimebound.cpp +++ clang/test/SemaCXX/attr-lifetimebound.cpp @@ -113,3 +113,55 @@ std::map<std::string, std::string> m; const std::string &v = findOrDefault(m, "foo"s, "bar"s); // expected-warning {{temporary bound to local reference 'v'}} } + +// definitions for std::move, std::forward et al. +namespace std { +inline namespace foo { + +template <class T> struct remove_reference { typedef T type; }; +template <class T> struct remove_reference<T&> { typedef T type; }; +template <class T> struct remove_reference<T&&> { typedef T type; }; + +template <class T> constexpr typename remove_reference<T>::type &&move(T &&t) { + return static_cast<typename remove_reference<T>::type>(t); +} + +template <class T> constexpr T&& forward(typename remove_reference<T>::type& t) { + return static_cast<T&&>(t); +} + +template <class T> constexpr T&& forward(typename remove_reference<T>::type&& t) { + return static_cast<T&&>(t); +} + +template<class T> constexpr const T &as_const(T &x) { + return x; +} + +template<class T, bool RValueRef> struct PickRef { using type = typename remove_reference<T>::type&; }; +template<class T> struct PickRef<T, true> { using type = typename remove_reference<T>::type&&; }; + +template<class T> auto move_if_noexcept(T &t) -> typename PickRef<T, noexcept(T(static_cast<T&&>(t)))>::type { + return static_cast<typename PickRef<T, noexcept(T(static_cast<T&&>(t)))>::type>(t); +} + +} // inline namespace foo +} // namespace std + +namespace move_forward_as_const_examples { + struct S { + S &self() [[clang::lifetimebound]] { return *this; } + }; + + S &&Move = std::move(S{}); // expected-warning {{temporary bound to local reference 'Move' will be destroyed at the end of the full-expression}} + S MoveOk = std::move(S{}); + + S &&Forward = std::forward<S&&>(S{}); // expected-warning {{temporary bound to local reference 'Forward' will be destroyed at the end of the full-expression}} + S ForwardOk = std::forward<S&&>(S{}); + + const S &Const = std::as_const(S{}.self()); // expected-warning {{temporary bound to local reference 'Const' will be destroyed at the end of the full-expression}} + const S ConstOk = std::as_const(S{}.self()); + + S &&MoveIfNoExcept = std::move_if_noexcept(S{}.self()); // expected-warning {{temporary bound to local reference 'MoveIfNoExcept' will be destroyed at the end of the full-expression}} + S MoveIfNoExceptOk = std::move_if_noexcept(S{}.self()); +} // namespace move_forward_as_const_examples Index: clang/lib/Sema/SemaDecl.cpp =================================================================== --- clang/lib/Sema/SemaDecl.cpp +++ clang/lib/Sema/SemaDecl.cpp @@ -16180,6 +16180,21 @@ default: break; } + + // Add lifetime attribute to std::move, std::fowrard et al. + switch (BuiltinID) { + case Builtin::BIas_const: + case Builtin::BIforward: + case Builtin::BImove: + case Builtin::BImove_if_noexcept: + if (ParmVarDecl *P = FD->getParamDecl(0u); + !P->hasAttr<LifetimeBoundAttr>()) + P->addAttr( + LifetimeBoundAttr::CreateImplicit(Context, FD->getLocation())); + break; + default: + break; + } } AddKnownFunctionAttributesForReplaceableGlobalAllocationFunction(FD);
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits