llvmbot wrote:

<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang-analysis

@llvm/pr-subscribers-clang-temporal-safety

Author: Utkarsh Saxena (usx95)

<details>
<summary>Changes</summary>

Prevent false positives in lifetime safety analysis when variables are moved 
using `std::move`.

When a value is moved using `std::move`, ownership is transferred from the 
original variable to another. The lifetime safety analysis was previously 
generating false positives by warning about use-after-lifetime when the 
original variable was destroyed after being moved. This change prevents those 
false positives by tracking moved declarations and exempting them from loan 
expiration checks.

- Added tracking for declarations that have been moved via `std::move` in the 
`FactsGenerator` class
- Added a `MovedDecls` set to track moved declarations in a flow-insensitive 
manner
- Implemented detection of `std::move` calls in `VisitCallExpr`
- Modified `handleLifetimeEnds` to skip loans for declarations that have been 
moved
- Added a test case demonstrating that warnings are suppressed for moved 
variables

```cpp
void silenced() {
  MyObj b;
  View v;
  {
    MyObj a;
    v = a;
    b = std::move(a); // No warning for 'a' being moved.
  }
  (void)v;
}
```

Fixes https://github.com/llvm/llvm-project/issues/152520

---
Full diff: https://github.com/llvm/llvm-project/pull/170007.diff


3 Files Affected:

- (modified) 
clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h (+5) 
- (modified) clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp (+23) 
- (modified) clang/test/Sema/warn-lifetime-safety.cpp (+18) 


``````````diff
diff --git 
a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
index 5b5626020e772..0c0581239ce34 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
@@ -101,6 +101,11 @@ class FactsGenerator : public 
ConstStmtVisitor<FactsGenerator> {
   // corresponding to the left-hand side is updated to be a "write", thereby
   // exempting it from the check.
   llvm::DenseMap<const DeclRefExpr *, UseFact *> UseFacts;
+
+  // Tracks declarations that have been moved via std::move. This is used to
+  // prevent false positives when the original owner is destroyed after the
+  // value has been moved. This tracking is flow-insensitive.
+  llvm::DenseSet<const ValueDecl *> MovedDecls;
 };
 
 } // namespace clang::lifetimes::internal
diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp 
b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
index b27dcb6163449..ba88af2418056 100644
--- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
@@ -163,9 +163,27 @@ void FactsGenerator::VisitCXXMemberCallExpr(const 
CXXMemberCallExpr *MCE) {
   }
 }
 
+static bool isStdMove(const FunctionDecl *FD) {
+  return FD && FD->isInStdNamespace() && FD->getIdentifier() &&
+         FD->getName() == "move";
+}
+
 void FactsGenerator::VisitCallExpr(const CallExpr *CE) {
   handleFunctionCall(CE, CE->getDirectCallee(),
                      {CE->getArgs(), CE->getNumArgs()});
+  // Track declarations that are moved via std::move.
+  // This is a flow-insensitive approximation: once a declaration is moved
+  // anywhere in the function, it's treated as moved everywhere. This can lead
+  // to false negatives on control flow paths where the value is not actually
+  // moved, but these are considered lower priority than the false positives
+  // this tracking prevents.
+  // TODO: The ideal solution would be flow-sensitive ownership tracking that
+  // records where values are moved from and to, but this is more complex.
+  if (isStdMove(CE->getDirectCallee()))
+    if (CE->getNumArgs() == 1)
+      if (auto *DRE =
+              dyn_cast<DeclRefExpr>(CE->getArg(0)->IgnoreParenImpCasts()))
+        MovedDecls.insert(DRE->getDecl());
 }
 
 void FactsGenerator::VisitCXXNullPtrLiteralExpr(
@@ -341,6 +359,11 @@ void FactsGenerator::handleLifetimeEnds(const 
CFGLifetimeEnds &LifetimeEnds) {
   // Iterate through all loans to see if any expire.
   for (const auto &Loan : FactMgr.getLoanMgr().getLoans()) {
     const AccessPath &LoanPath = Loan.Path;
+    // Skip loans for declarations that have been moved. When a value is moved,
+    // the original owner no longer has ownership and its destruction should 
not
+    // cause the loan to expire, preventing false positives.
+    if (MovedDecls.contains(LoanPath.D))
+      continue;
     // Check if the loan is for a stack variable and if that variable
     // is the one being destructed.
     if (LoanPath.D == LifetimeEndsVD)
diff --git a/clang/test/Sema/warn-lifetime-safety.cpp 
b/clang/test/Sema/warn-lifetime-safety.cpp
index f22c73cfeb784..97a79cc4ce102 100644
--- a/clang/test/Sema/warn-lifetime-safety.cpp
+++ b/clang/test/Sema/warn-lifetime-safety.cpp
@@ -1,9 +1,14 @@
 // RUN: %clang_cc1 -fsyntax-only -fexperimental-lifetime-safety 
-Wexperimental-lifetime-safety -Wno-dangling -verify %s
 
+#include "Inputs/lifetime-analysis.h"
+
 struct View;
 
 struct [[gsl::Owner]] MyObj {
   int id;
+  MyObj();
+  MyObj(int);
+  MyObj(const MyObj&);
   ~MyObj() {}  // Non-trivial destructor
   MyObj operator+(MyObj);
   
@@ -1297,3 +1302,16 @@ void add(int c, MyObj* node) {
   arr[4] = node;
 }
 } // namespace CppCoverage
+
+namespace do_not_warn_on_std_move {
+void silenced() {
+  MyObj b;
+  View v;
+  {
+    MyObj a;
+    v = a;
+    b = std::move(a); // No warning for 'a' being moved.
+  }
+  (void)v;
+}
+} // namespace do_not_warn_on_std_move

``````````

</details>


https://github.com/llvm/llvm-project/pull/170007
_______________________________________________
llvm-branch-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits

Reply via email to