diff --git a/clang/lib/Analysis/ThreadSafety.cpp b/clang/lib/Analysis/ThreadSafety.cpp index e0ff23df5ab4bb..6e518e7d38ef1d 100644 --- a/clang/lib/Analysis/ThreadSafety.cpp +++ b/clang/lib/Analysis/ThreadSafety.cpp @@ -905,11 +905,7 @@ class ScopedLockableFactEntry : public FactEntry { ScopedLockableFactEntry(const CapabilityExpr &CE, SourceLocation Loc) : FactEntry(CE, LK_Exclusive, Loc, false) {} - void addExclusiveLock(const CapabilityExpr &M) { - UnderlyingMutexes.emplace_back(M.sexpr(), UCK_Acquired); - } - - void addSharedLock(const CapabilityExpr &M) { + void addLock(const CapabilityExpr &M) { UnderlyingMutexes.emplace_back(M.sexpr(), UCK_Acquired); } @@ -1801,7 +1797,7 @@ void BuildLockset::handleCall(const Expr *Exp, const NamedDecl *D, SourceLocation Loc = Exp->getExprLoc(); CapExprSet ExclusiveLocksToAdd, SharedLocksToAdd; CapExprSet ExclusiveLocksToRemove, SharedLocksToRemove, GenericLocksToRemove; - CapExprSet ScopedExclusiveReqs, ScopedSharedReqs; + CapExprSet ScopedReqsAndExcludes; StringRef CapDiagKind = "mutex"; // Figure out if we're constructing an object of scoped lockable class @@ -1892,19 +1888,20 @@ void BuildLockset::handleCall(const Expr *Exp, const NamedDecl *D, POK_FunctionCall, ClassifyDiagnostic(A), Exp->getExprLoc()); // use for adopting a lock - if (isScopedVar) { - Analyzer->getMutexIDs(A->isShared() ? ScopedSharedReqs - : ScopedExclusiveReqs, - A, Exp, D, VD); - } + if (isScopedVar) + Analyzer->getMutexIDs(ScopedReqsAndExcludes, A, Exp, D, VD); } break; } case attr::LocksExcluded: { const auto *A = cast(At); - for (auto *Arg : A->args()) + for (auto *Arg : A->args()) { warnIfMutexHeld(D, Exp, Arg, ClassifyDiagnostic(A)); + // use for deferring a lock + if (isScopedVar) + Analyzer->getMutexIDs(ScopedReqsAndExcludes, A, Exp, D, VD); + } break; } @@ -1944,13 +1941,11 @@ void BuildLockset::handleCall(const Expr *Exp, const NamedDecl *D, auto ScopedEntry = std::make_unique(Scp, MLoc); for (const auto &M : ExclusiveLocksToAdd) - ScopedEntry->addExclusiveLock(M); - for (const auto &M : ScopedExclusiveReqs) - ScopedEntry->addExclusiveLock(M); + ScopedEntry->addLock(M); for (const auto &M : SharedLocksToAdd) - ScopedEntry->addSharedLock(M); - for (const auto &M : ScopedSharedReqs) - ScopedEntry->addSharedLock(M); + ScopedEntry->addLock(M); + for (const auto &M : ScopedReqsAndExcludes) + ScopedEntry->addLock(M); for (const auto &M : ExclusiveLocksToRemove) ScopedEntry->addExclusiveUnlock(M); for (const auto &M : SharedLocksToRemove) diff --git a/clang/test/SemaCXX/warn-thread-safety-analysis.cpp b/clang/test/SemaCXX/warn-thread-safety-analysis.cpp index cdb22cd22a9961..02700147a032e4 100644 --- a/clang/test/SemaCXX/warn-thread-safety-analysis.cpp +++ b/clang/test/SemaCXX/warn-thread-safety-analysis.cpp @@ -2625,9 +2625,12 @@ void Foo::test5() { namespace RelockableScopedLock { +class DeferTraits {}; + class SCOPED_LOCKABLE RelockableExclusiveMutexLock { public: RelockableExclusiveMutexLock(Mutex *mu) EXCLUSIVE_LOCK_FUNCTION(mu); + RelockableExclusiveMutexLock(Mutex *mu, DeferTraits) LOCKS_EXCLUDED(mu); ~RelockableExclusiveMutexLock() EXCLUSIVE_UNLOCK_FUNCTION(); void Lock() EXCLUSIVE_LOCK_FUNCTION(); @@ -2639,6 +2642,7 @@ struct ExclusiveTraits {}; class SCOPED_LOCKABLE RelockableMutexLock { public: + RelockableMutexLock(Mutex *mu, DeferTraits) LOCKS_EXCLUDED(mu); RelockableMutexLock(Mutex *mu, SharedTraits) SHARED_LOCK_FUNCTION(mu); RelockableMutexLock(Mutex *mu, ExclusiveTraits) EXCLUSIVE_LOCK_FUNCTION(mu); ~RelockableMutexLock() UNLOCK_FUNCTION(); @@ -2669,6 +2673,13 @@ void relock() { x = 4; } +void deferLock() { + RelockableExclusiveMutexLock scope(&mu, DeferTraits{}); + x = 2; // expected-warning {{writing variable 'x' requires holding mutex 'mu' exclusively}} + scope.Lock(); + x = 3; +} + void relockExclusive() { RelockableMutexLock scope(&mu, SharedTraits{}); print(x); @@ -2703,6 +2714,14 @@ void relockShared() { x = 5; } +void deferLockShared() { + RelockableMutexLock scope(&mu, DeferTraits{}); + print(x); // expected-warning {{reading variable 'x' requires holding mutex 'mu'}} + scope.ReaderLock(); + print(x); + x = 2; // expected-warning {{writing variable 'x' requires holding mutex 'mu' exclusively}} +} + void doubleUnlock() { RelockableExclusiveMutexLock scope(&mu); scope.Unlock();