llvmbot wrote:



Author: Ziqing Luo (ziqingluo-90)


The commit 54bfd04846156dbd5e0a6b88f539c3d4569a455f introduces general handling 
of constructor and destructor calls.  Consider a pair of TEMPORARY constructor 
and destructor calls of a "SCOPED_CAPABILITY" object.  At the constructor call, 
a resource representing the object itself is added to the state.  But since it 
is a temporary object, the analyzer may not be able to find an AST part to 
identify the resource (As opposed to the normal case of using `VarDecl` as an 
identifier). Later at the destructor call, the resource needs to be removed 
from the state.  Since the resource cannot be identified by anything other than 
itself, the analyzer maintains a map from constructor calls to their created 
resources so that the analyzer could get the proper resource for removal at the 
destructor call via look up the map.  The issue is that the map lives within a 
CFG block.  That means in case the pair of temporary constructor and destructor 
calls are not in one CFG block, the analyzer is not able to remove the proper 
resource at the destructor call.  Although the two calls stay in one CFG block 
usually, they separate in the following example:

if (f(MutexLock{lock}) || x) { // &lt;-- the temporary Ctor and Dtor of 
MutexLock are in different CFG blocks


The fix is simple. It extends the life of the map to that of the entire CFG.

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

2 Files Affected:

- (modified) clang/lib/Analysis/ThreadSafety.cpp (+13-5) 
- (modified) clang/test/SemaCXX/warn-thread-safety-analysis.cpp (+9) 

diff --git a/clang/lib/Analysis/ThreadSafety.cpp 
index 7fdf22c2f3919cb..9549d1b398dfa08 100644
--- a/clang/lib/Analysis/ThreadSafety.cpp
+++ b/clang/lib/Analysis/ThreadSafety.cpp
@@ -1530,7 +1530,7 @@ void ThreadSafetyAnalyzer::getEdgeLockset(FactSet& Result,
 namespace {
+using ConstructedObjectMapTy = llvm::SmallDenseMap<const Expr *, 
til::LiteralPtr *>;
 /// We use this class to visit different types of expressions in
 /// CFGBlocks, and build up the lockset.
 /// An expression may cause us to add or remove locks from the lockset, or else
@@ -1544,7 +1544,10 @@ class BuildLockset : public 
ConstStmtVisitor<BuildLockset> {
   // The fact set for the function on exit.
   const FactSet &FunctionExitFSet;
   /// Maps constructed objects to `this` placeholder prior to initialization.
-  llvm::SmallDenseMap<const Expr *, til::LiteralPtr *> ConstructedObjects;
+  /// The map lives longer than this builder so this is a reference to an
+  /// existing one. The map lives so long as the CFG while this builder is for
+  /// one CFG block.
+  ConstructedObjectMapTy &ConstructedObjects;
   LocalVariableMap::Context LVarCtx;
   unsigned CtxIndex;
@@ -1569,9 +1572,11 @@ class BuildLockset : public 
ConstStmtVisitor<BuildLockset> {
   BuildLockset(ThreadSafetyAnalyzer *Anlzr, CFGBlockInfo &Info,
-               const FactSet &FunctionExitFSet)
+               const FactSet &FunctionExitFSet,
+               ConstructedObjectMapTy &ConstructedObjects)
       : ConstStmtVisitor<BuildLockset>(), Analyzer(Anlzr), FSet(Info.EntrySet),
-        FunctionExitFSet(FunctionExitFSet), LVarCtx(Info.EntryContext),
+        FunctionExitFSet(FunctionExitFSet),
+        ConstructedObjects(ConstructedObjects), LVarCtx(Info.EntryContext),
         CtxIndex(Info.EntryIndex) {}
   void VisitUnaryOperator(const UnaryOperator *UO);
@@ -2392,6 +2397,8 @@ void 
ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {
   for (const auto &Lock : LocksReleased)
     ExpectedFunctionExitSet.removeLock(FactMan, Lock);
+  ConstructedObjectMapTy ConstructedObjects;
   for (const auto *CurrBlock : *SortedGraph) {
     unsigned CurrBlockID = CurrBlock->getBlockID();
     CFGBlockInfo *CurrBlockInfo = &BlockInfo[CurrBlockID];
@@ -2451,7 +2458,8 @@ void 
ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {
     if (!CurrBlockInfo->Reachable)
-    BuildLockset LocksetBuilder(this, *CurrBlockInfo, ExpectedFunctionExitSet);
+    BuildLockset LocksetBuilder(this, *CurrBlockInfo, ExpectedFunctionExitSet,
+                                ConstructedObjects);
     // Visit all the statements in the basic block.
     for (const auto &BI : *CurrBlock) {
diff --git a/clang/test/SemaCXX/warn-thread-safety-analysis.cpp 
index 205cfa284f6c9c9..691b8733d58fd03 100644
--- a/clang/test/SemaCXX/warn-thread-safety-analysis.cpp
+++ b/clang/test/SemaCXX/warn-thread-safety-analysis.cpp
@@ -1702,6 +1702,8 @@ struct TestScopedLockable {
   bool getBool();
+  bool check(MutexLock);
   void foo1() {
     MutexLock mulock(&mu1);
     a = 5;
@@ -1718,6 +1720,13 @@ struct TestScopedLockable {
     MutexLock{&mu1}, a = 5;
+  void temporary2(int x) {
+    if (check(MutexLock{&mu1}) || x) {
+    }
+    check(MutexLock{&mu1});
+  }
   void lifetime_extension() {
     const MutexLock &mulock = MutexLock(&mu1);
     a = 5;



cfe-commits mailing list

Reply via email to