llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang Author: Zhijie Wang (aeft) <details> <summary>Changes</summary> std::move and related casts (std::forward, std::forward_like, std::move_if_noexcept, std::as_const) are reference casts: the result refers to the same object as the argument. Flow all origin levels for this family. Fixes #<!-- -->191954 --- Full diff: https://github.com/llvm/llvm-project/pull/199600.diff 5 Files Affected: - (modified) clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h (+5) - (modified) clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp (+12-5) - (modified) clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp (+15) - (modified) clang/test/Sema/Inputs/lifetime-analysis.h (+3) - (modified) clang/test/Sema/warn-lifetime-safety.cpp (+37-5) ``````````diff diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h index a97df7a08dfeb..47fcd5dbfd569 100644 --- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h +++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h @@ -106,6 +106,11 @@ bool destructsFirstArg(const FunctionDecl &FD); /// that can propagate the stored lambda's origins. bool isStdCallableWrapperType(const CXXRecordDecl *RD); +/// Returns true for std reference-cast builtins (e.g., std::move). Their result +/// refers to the same object as the argument, so all origins propagate from +/// argument to result. +bool isStdReferenceCast(const FunctionDecl *FD); + } // namespace clang::lifetimes #endif // LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMEANNOTATIONS_H diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp index 9038f56689779..b79442836c461 100644 --- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp +++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp @@ -961,11 +961,18 @@ void FactsGenerator::handleFunctionCall(const Expr *Call, ArgList->peelOuterOrigin()->getOuterOriginID(), KillSrc)); KillSrc = false; } else if (IsArgLifetimeBound(I)) { - // Lifetimebound on a non-GSL-ctor function means the returned - // pointer/reference itself must not outlive the arguments. This - // only constraints the top-level origin. - CurrentBlockFacts.push_back(FactMgr.createFact<OriginFlowFact>( - CallList->getOuterOriginID(), ArgList->getOuterOriginID(), KillSrc)); + if (isStdReferenceCast(FD)) { + // e.g., std::move(p): the result refers to the same object as p, so + // flow inner origins too. + flow(CallList, ArgList, KillSrc); + } else { + // Lifetimebound on a non-GSL-ctor function means the returned + // pointer/reference itself must not outlive the arguments. This + // only constrains the top-level origin. + CurrentBlockFacts.push_back(FactMgr.createFact<OriginFlowFact>( + CallList->getOuterOriginID(), ArgList->getOuterOriginID(), + KillSrc)); + } KillSrc = false; } } diff --git a/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp b/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp index 2f26e77d5a0eb..6a52616c5d590 100644 --- a/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp +++ b/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp @@ -460,4 +460,19 @@ bool isStdCallableWrapperType(const CXXRecordDecl *RD) { return Name == "function" || Name == "move_only_function"; } +bool isStdReferenceCast(const FunctionDecl *FD) { + if (!FD) + return false; + switch (FD->getBuiltinID()) { + case Builtin::BImove: + case Builtin::BImove_if_noexcept: + case Builtin::BIforward: + case Builtin::BIforward_like: + case Builtin::BIas_const: + return true; + default: + return false; + } +} + } // namespace clang::lifetimes diff --git a/clang/test/Sema/Inputs/lifetime-analysis.h b/clang/test/Sema/Inputs/lifetime-analysis.h index 2ae6ed38714b7..4d727ae9499d6 100644 --- a/clang/test/Sema/Inputs/lifetime-analysis.h +++ b/clang/test/Sema/Inputs/lifetime-analysis.h @@ -37,6 +37,9 @@ ForwardIt1 search( ForwardIt1 first, ForwardIt1 last, template<typename T> typename remove_reference<T>::type &&move(T &&t) noexcept; +template<typename T> +T &&forward(typename remove_reference<T>::type &t) noexcept; + template <typename C> auto data(const C &c) -> decltype(c.data()); diff --git a/clang/test/Sema/warn-lifetime-safety.cpp b/clang/test/Sema/warn-lifetime-safety.cpp index 0619fda738fa4..486ac29a6b818 100644 --- a/clang/test/Sema/warn-lifetime-safety.cpp +++ b/clang/test/Sema/warn-lifetime-safety.cpp @@ -3249,15 +3249,12 @@ std::function<void()> chained_copy_assign() { return f3; // expected-note {{returned here}} } -// FIXME: False negative. std::move's lifetimebound handling in -// `handleFunctionCall` only flows the outermost origin, missing inner origins -// that carry the lambda's loans. std::function<void()> move_assign() { int x; - std::function<void()> f = [&x]() { (void)x; }; // Should warn. + std::function<void()> f = [&x]() { (void)x; }; // expected-warning {{address of stack memory is returned later}} std::function<void()> f2 = []() {}; f2 = std::move(f); - return f2; + return f2; // expected-note {{returned here}} } std::function<void()> reassign_safe_then_unsafe() { @@ -3376,3 +3373,38 @@ void deref_use_after_scope() { } } // namespace GH188832 + +namespace GH191954 { + int* return_moved_pointer() { + int x; + int* f = &x; // expected-warning {{address of stack memory is returned later}} + int* a; + a = std::move(f); + return a; // expected-note {{returned here}} + } + + int* return_moved_pointer2() { + int x; + int* f = &x; // expected-warning {{address of stack memory is returned later}} + return std::move(f); // expected-note {{returned here}} + } + + View return_moved_view() { + MyObj o; + View v(o); // expected-warning {{address of stack memory is returned later}} + View v2 = std::move(v); + return v2; // expected-note {{returned here}} + } + + int* return_forwarded_pointer() { + int x; + int* f = &x; // expected-warning {{address of stack memory is returned later}} + return std::forward<int*>(f); // expected-note {{returned here}} + } + + int g; + int* return_moved_pointer_to_global() { + int* f = &g; + return std::move(f); + } +} // namespace GH191954 `````````` </details> https://github.com/llvm/llvm-project/pull/199600 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
