[clang] [LifetimeSafety] Generalize invalidating member function detection (PR #194907)

via cfe-commits cfe-commits at lists.llvm.org
Wed Apr 29 09:48:30 PDT 2026


llvmbot wrote:


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

@llvm/pr-subscribers-clang-analysis

Author: NeKon69

<details>
<summary>Changes</summary>

This PR adds support for invalidating references after reassigning a `unique_ptr` or calling its `reset` member function.

Previously, invalidation handling was limited to container-like types. This PR generalizes the helper for detecting invalidating member calls and adds `unique_ptr`'s `reset` member function as an invalidating one. Since `unique_ptr` is now handled by this helper, reassignment through `operator=` is also treated as invalidating.

Fixes #<!-- -->184630

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


5 Files Affected:

- (modified) clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h (+6-6) 
- (modified) clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp (+1-1) 
- (modified) clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp (+8-4) 
- (modified) clang/test/Sema/Inputs/lifetime-analysis.h (+3) 
- (modified) clang/test/Sema/warn-lifetime-safety-invalidations.cpp (+34) 


``````````diff
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h
index 098c15f4a7fb4..433239fb385a3 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h
@@ -71,14 +71,14 @@ bool isGslOwnerType(QualType QT);
 // when ownership is manually transferred.
 bool isUniquePtrRelease(const CXXMethodDecl &MD);
 
-// Returns true if the given method invalidates references to container
-// elements (e.g. vector::push_back). Methods that only invalidate iterators
-// but not references (e.g. unordered_map::emplace) are not considered
-// invalidating here.
+// Returns true if the given method invalidates references tracked by lifetime
+// analysis (e.g. vector::push_back). Methods that only invalidate iterators but
+// not references (e.g. unordered_map::emplace) are not considered invalidating
+// here.
 //
-// Invalidation rules are based on:
+// Container invalidation rules are based on:
 // https://en.cppreference.com/w/cpp/container#Iterator_invalidation
-bool isContainerInvalidationMethod(const CXXMethodDecl &MD);
+bool isInvalidationMethod(const CXXMethodDecl &MD);
 
 /// Returns true for standard library callable wrappers (e.g., std::function)
 /// that can propagate the stored lambda's origins.
diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
index efdb1a1691ae3..2c7cc6b38b3e1 100644
--- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
@@ -771,7 +771,7 @@ void FactsGenerator::handleInvalidatingCall(const Expr *Call,
   if (!MD || !MD->isInstance())
     return;
 
-  if (!isContainerInvalidationMethod(*MD))
+  if (!isInvalidationMethod(*MD))
     return;
   // Heuristics to turn-down false positives.
   auto *DRE = dyn_cast<DeclRefExpr>(Args[0]);
diff --git a/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp b/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
index 27d95821dd0b4..e1cc6a098694c 100644
--- a/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
@@ -277,7 +277,7 @@ bool isUniquePtrRelease(const CXXMethodDecl &MD) {
          MD.getNumParams() == 0 && isStdUniquePtr(*MD.getParent());
 }
 
-bool isContainerInvalidationMethod(const CXXMethodDecl &MD) {
+bool isInvalidationMethod(const CXXMethodDecl &MD) {
   const CXXRecordDecl *RD = MD.getParent();
   if (!isInStlNamespace(RD))
     return false;
@@ -342,11 +342,14 @@ bool isContainerInvalidationMethod(const CXXMethodDecl &MD) {
                                          // Assignment
                                          "replace"};
 
-  const StringRef ContainerName = getName(*RD);
+  static const llvm::StringSet<> UniquePtr = {// Reallocation
+                                              "reset"};
+
+  const StringRef RecordName = getName(*RD);
   // TODO: Consider caching this lookup by CXXMethodDecl pointer if this
   // StringSwitch becomes a performance bottleneck.
   const llvm::StringSet<> *InvalidatingMethods =
-      llvm::StringSwitch<const llvm::StringSet<> *>(ContainerName)
+      llvm::StringSwitch<const llvm::StringSet<> *>(RecordName)
           .Case("vector", &Vector)
           .Case("basic_string", &String)
           .Case("deque", &Deque)
@@ -356,6 +359,7 @@ bool isContainerInvalidationMethod(const CXXMethodDecl &MD) {
                  &NodeBased)
           .Cases({"flat_map", "flat_set", "flat_multimap", "flat_multiset"},
                  &Flat)
+          .Case("unique_ptr", &UniquePtr)
           .Default(nullptr);
 
   if (!InvalidatingMethods)
@@ -371,7 +375,7 @@ bool isContainerInvalidationMethod(const CXXMethodDecl &MD) {
     case OO_Subscript: // operator[] : Invalidation only for
                        // `flat_map` (Insert-or-access).
                        // `map` and `unordered_map` are excluded.
-      return ContainerName == "flat_map";
+      return RecordName == "flat_map";
     default:
       return false;
     }
diff --git a/clang/test/Sema/Inputs/lifetime-analysis.h b/clang/test/Sema/Inputs/lifetime-analysis.h
index 14226c7ff1f13..c7473d2928adf 100644
--- a/clang/test/Sema/Inputs/lifetime-analysis.h
+++ b/clang/test/Sema/Inputs/lifetime-analysis.h
@@ -40,6 +40,7 @@ template<typename T, int N>
 T *begin(T (&array)[N]);
 
 using size_t = decltype(sizeof(0));
+using nullptr_t = decltype(nullptr);
 
 template<typename T>
 struct initializer_list {
@@ -199,6 +200,8 @@ struct unique_ptr {
   explicit unique_ptr(T*);
   unique_ptr(unique_ptr<T>&&);
   unique_ptr& operator=(unique_ptr<T>&&);
+  unique_ptr& operator=(std::nullptr_t);
+  void reset();
   ~unique_ptr();
   T* release();
   T &operator*();
diff --git a/clang/test/Sema/warn-lifetime-safety-invalidations.cpp b/clang/test/Sema/warn-lifetime-safety-invalidations.cpp
index 973e095fb68b4..19e2c9299a650 100644
--- a/clang/test/Sema/warn-lifetime-safety-invalidations.cpp
+++ b/clang/test/Sema/warn-lifetime-safety-invalidations.cpp
@@ -539,3 +539,37 @@ void function_captured_ref_invalidated() {
 }
 
 } // namespace callable_wrappers
+
+namespace unique_ptr_invalidation {
+
+void invalid_after_reset() {
+  std::unique_ptr<int> up(new int);
+  int *p = up.get(); // expected-warning {{object whose reference is captured is later invalidated}}
+  up.reset();        // expected-note {{invalidated here}}
+  (void)*p;          // expected-note {{later used here}}
+}
+
+void invalid_after_move_assign() {
+  std::unique_ptr<int> up(new int);
+  std::unique_ptr<int> other(new int);
+  int *p = up.get();     // expected-warning {{object whose reference is captured is later invalidated}}
+  up = std::move(other); // expected-note {{invalidated here}}
+  (void)*p;              // expected-note {{later used here}}
+}
+
+void invalid_after_null_assign() {
+  std::unique_ptr<int> up(new int);
+  int *p = up.get(); // expected-warning {{object whose reference is captured is later invalidated}}
+  up = nullptr;      // expected-note {{invalidated here}}
+  (void)*p;          // expected-note {{later used here}}
+}
+
+void invalid_after_ternary_reset(bool flag) {
+  std::unique_ptr<int> up(new int);
+  std::unique_ptr<int> other(new int);
+  int *p = flag ? up.get() : other.get(); // expected-warning {{object whose reference is captured is later invalidated}}
+  up.reset();                             // expected-note {{invalidated here}}
+  (void)*p;                               // expected-note {{later used here}}
+}
+
+} // namespace unique_ptr_invalidation

``````````

</details>


https://github.com/llvm/llvm-project/pull/194907


More information about the cfe-commits mailing list