Skip to content

Commit 7df9ba1

Browse files
committed
Thread safety analysis: Skip functions acquiring/releasing parameters
The analysis already excludes functions with a zero-argument acquire or release attribute. According to the requirements enforced by -Wthread-safety-attributes, these are methods of a capability class where the attribute implicitly refers to `this`. C doesn't have class methods, so the lock/unlock implementations are free functions. If we disable the analysis for all free functions with attributes, this would obviously exclude too much. But maybe we can exclude functions where the attribute directly refers to a parameter. Mutex implementations should typically do that, and I don't see why other functions should. (Typically, other functions acquire or release a global mutex or a member of a parameter.) Fixes #139933.
1 parent d0acddb commit 7df9ba1

File tree

2 files changed

+46
-2
lines changed

2 files changed

+46
-2
lines changed

clang/lib/Analysis/ThreadSafety.cpp

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2344,6 +2344,20 @@ static bool neverReturns(const CFGBlock *B) {
23442344
return false;
23452345
}
23462346

2347+
static bool attrArgsForImpl(llvm::iterator_range<clang::Expr **> Args) {
2348+
// An attribute with no arguments implicitly refers to 'this'.
2349+
if (Args.empty())
2350+
return true;
2351+
2352+
return llvm::all_of(Args, [](const Expr *E) {
2353+
if (isa<CXXThisExpr>(E))
2354+
return true;
2355+
if (const auto* DRE = dyn_cast<DeclRefExpr>(E))
2356+
return isa<ParmVarDecl>(DRE->getDecl());
2357+
return false;
2358+
});
2359+
}
2360+
23472361
/// Check a function's CFG for thread-safety violations.
23482362
///
23492363
/// We traverse the blocks in the CFG, compute the set of mutexes that are held
@@ -2421,13 +2435,13 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {
24212435
} else if (const auto *A = dyn_cast<ReleaseCapabilityAttr>(Attr)) {
24222436
// UNLOCK_FUNCTION() is used to hide the underlying lock implementation.
24232437
// We must ignore such methods.
2424-
if (A->args_size() == 0)
2438+
if (attrArgsForImpl(A->args()))
24252439
return;
24262440
getMutexIDs(A->isShared() ? SharedLocksToAdd : ExclusiveLocksToAdd, A,
24272441
nullptr, D);
24282442
getMutexIDs(LocksReleased, A, nullptr, D);
24292443
} else if (const auto *A = dyn_cast<AcquireCapabilityAttr>(Attr)) {
2430-
if (A->args_size() == 0)
2444+
if (attrArgsForImpl(A->args()))
24312445
return;
24322446
getMutexIDs(A->isShared() ? SharedLocksAcquired
24332447
: ExclusiveLocksAcquired,

clang/test/SemaCXX/warn-thread-safety-analysis.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2401,6 +2401,36 @@ class LOCKABLE MyLock2 {
24012401
} // end namespace SelfLockingTest
24022402

24032403

2404+
namespace ExcludeImplementation {
2405+
2406+
class LOCKABLE Mutex2 {
2407+
public:
2408+
// We don't require EXCLUSIVE_(UN)LOCK_FUNCTION(impl) on these methods.
2409+
void Lock() EXCLUSIVE_LOCK_FUNCTION() { impl.Lock(); }
2410+
void Unlock() EXCLUSIVE_UNLOCK_FUNCTION() { impl.Unlock(); }
2411+
2412+
void LockAlt() EXCLUSIVE_LOCK_FUNCTION(this) { impl.Lock(); }
2413+
void UnlockAlt() EXCLUSIVE_UNLOCK_FUNCTION(this) { impl.Unlock(); }
2414+
2415+
private:
2416+
Mutex impl;
2417+
};
2418+
2419+
struct LOCKABLE Mutex3 {
2420+
Mutex impl;
2421+
};
2422+
2423+
void Lock(Mutex3* mu) EXCLUSIVE_LOCK_FUNCTION(mu) {
2424+
mu->impl.Lock();
2425+
}
2426+
2427+
void Unlock(Mutex3* mu) EXCLUSIVE_UNLOCK_FUNCTION(mu) {
2428+
mu->impl.Unlock();
2429+
}
2430+
2431+
} // namespace ExcludeImplementation
2432+
2433+
24042434
namespace InvalidNonstatic {
24052435

24062436
// Forward decl here causes bogus "invalid use of non-static data member"

0 commit comments

Comments
 (0)