aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZiqing Luo <ziqing@udel.edu>2023-12-08 10:29:37 -0800
committerGitHub <noreply@github.com>2023-12-08 10:29:37 -0800
commita341e177cea1cee800793d357264f6f46a3b4979 (patch)
treefe5558c5bd41d9b12169f1576918fc03f9331e6d
parent6f9cb9a75ce20db2ee85cd22ddadc3bed2c450c0 (diff)
downloadllvm-a341e177cea1cee800793d357264f6f46a3b4979.zip
llvm-a341e177cea1cee800793d357264f6f46a3b4979.tar.gz
llvm-a341e177cea1cee800793d357264f6f46a3b4979.tar.bz2
Thread safety analysis: Fix a bug in handling temporary constructors (#74020)
Extends the lifetime of the map `ConstructedObjects` to be of the whole CFG so that the map can connect temporary Ctor and Dtor in different CFG blocks.
-rw-r--r--clang/lib/Analysis/ThreadSafety.cpp26
-rw-r--r--clang/test/SemaCXX/warn-thread-safety-analysis.cpp8
2 files changed, 21 insertions, 13 deletions
diff --git a/clang/lib/Analysis/ThreadSafety.cpp b/clang/lib/Analysis/ThreadSafety.cpp
index 7fdf22c..e25b843 100644
--- a/clang/lib/Analysis/ThreadSafety.cpp
+++ b/clang/lib/Analysis/ThreadSafety.cpp
@@ -1010,6 +1010,8 @@ class ThreadSafetyAnalyzer {
ThreadSafetyHandler &Handler;
const FunctionDecl *CurrentFunction;
LocalVariableMap LocalVarMap;
+ // Maps constructed objects to `this` placeholder prior to initialization.
+ llvm::SmallDenseMap<const Expr *, til::LiteralPtr *> ConstructedObjects;
FactManager FactMan;
std::vector<CFGBlockInfo> BlockInfo;
@@ -1543,8 +1545,6 @@ class BuildLockset : public ConstStmtVisitor<BuildLockset> {
FactSet FSet;
// 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;
LocalVariableMap::Context LVarCtx;
unsigned CtxIndex;
@@ -1808,7 +1808,7 @@ void BuildLockset::handleCall(const Expr *Exp, const NamedDecl *D,
std::pair<til::LiteralPtr *, StringRef> Placeholder =
Analyzer->SxBuilder.createThisPlaceholder(Exp);
[[maybe_unused]] auto inserted =
- ConstructedObjects.insert({Exp, Placeholder.first});
+ Analyzer->ConstructedObjects.insert({Exp, Placeholder.first});
assert(inserted.second && "Are we visiting the same expression again?");
if (isa<CXXConstructExpr>(Exp))
Self = Placeholder.first;
@@ -2128,10 +2128,10 @@ void BuildLockset::VisitDeclStmt(const DeclStmt *S) {
E = EWC->getSubExpr()->IgnoreParens();
E = UnpackConstruction(E);
- if (auto Object = ConstructedObjects.find(E);
- Object != ConstructedObjects.end()) {
+ if (auto Object = Analyzer->ConstructedObjects.find(E);
+ Object != Analyzer->ConstructedObjects.end()) {
Object->second->setClangDecl(VD);
- ConstructedObjects.erase(Object);
+ Analyzer->ConstructedObjects.erase(Object);
}
}
}
@@ -2140,11 +2140,11 @@ void BuildLockset::VisitDeclStmt(const DeclStmt *S) {
void BuildLockset::VisitMaterializeTemporaryExpr(
const MaterializeTemporaryExpr *Exp) {
if (const ValueDecl *ExtD = Exp->getExtendingDecl()) {
- if (auto Object =
- ConstructedObjects.find(UnpackConstruction(Exp->getSubExpr()));
- Object != ConstructedObjects.end()) {
+ if (auto Object = Analyzer->ConstructedObjects.find(
+ UnpackConstruction(Exp->getSubExpr()));
+ Object != Analyzer->ConstructedObjects.end()) {
Object->second->setClangDecl(ExtD);
- ConstructedObjects.erase(Object);
+ Analyzer->ConstructedObjects.erase(Object);
}
}
}
@@ -2487,15 +2487,15 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {
// Clean up constructed object even if there are no attributes to
// keep the number of objects in limbo as small as possible.
- if (auto Object = LocksetBuilder.ConstructedObjects.find(
+ if (auto Object = ConstructedObjects.find(
TD.getBindTemporaryExpr()->getSubExpr());
- Object != LocksetBuilder.ConstructedObjects.end()) {
+ Object != ConstructedObjects.end()) {
const auto *DD = TD.getDestructorDecl(AC.getASTContext());
if (DD->hasAttrs())
// TODO: the location here isn't quite correct.
LocksetBuilder.handleCall(nullptr, DD, Object->second,
TD.getBindTemporaryExpr()->getEndLoc());
- LocksetBuilder.ConstructedObjects.erase(Object);
+ ConstructedObjects.erase(Object);
}
break;
}
diff --git a/clang/test/SemaCXX/warn-thread-safety-analysis.cpp b/clang/test/SemaCXX/warn-thread-safety-analysis.cpp
index 205cfa2..dfb966d 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 lock2Bool(MutexLock);
+
void foo1() {
MutexLock mulock(&mu1);
a = 5;
@@ -1718,6 +1720,12 @@ struct TestScopedLockable {
MutexLock{&mu1}, a = 5;
}
+ void temporary_cfg(int x) {
+ // test the case where a pair of temporary Ctor and Dtor is in different CFG blocks
+ lock2Bool(MutexLock{&mu1}) || x;
+ MutexLock{&mu1}; // no-warn
+ }
+
void lifetime_extension() {
const MutexLock &mulock = MutexLock(&mu1);
a = 5;