[clang] [LifetimeSafety] Propagate inner origins through std::move and related casts (PR #199600)
via cfe-commits
cfe-commits at lists.llvm.org
Mon May 25 21:35:51 PDT 2026
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
More information about the cfe-commits
mailing list