From: Richard Trieu Date: Sat, 22 Sep 2018 01:50:52 +0000 (+0000) Subject: Update smart pointer detection for thread safety analysis. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=797bc2cafc595aa8f6909145bad94eb7b37faec9;p=clang Update smart pointer detection for thread safety analysis. Objects are determined to be smart pointers if they have both a star and arrow operator. Some implementations of smart pointers have these overloaded operators in a base class, while the check only searched the derived class. This fix will also look for the operators in the base class. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@342794 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp index f6df0d0e96..ff432a6fba 100644 --- a/lib/Sema/SemaDeclAttr.cpp +++ b/lib/Sema/SemaDeclAttr.cpp @@ -425,17 +425,36 @@ static bool isIntOrBool(Expr *Exp) { // Check to see if the type is a smart pointer of some kind. We assume // it's a smart pointer if it defines both operator-> and operator*. static bool threadSafetyCheckIsSmartPointer(Sema &S, const RecordType* RT) { - DeclContextLookupResult Res1 = RT->getDecl()->lookup( - S.Context.DeclarationNames.getCXXOperatorName(OO_Star)); - if (Res1.empty()) - return false; + auto IsOverloadedOperatorPresent = [&S](const RecordDecl *Record, + OverloadedOperatorKind Op) { + DeclContextLookupResult Result = + Record->lookup(S.Context.DeclarationNames.getCXXOperatorName(Op)); + return !Result.empty(); + }; - DeclContextLookupResult Res2 = RT->getDecl()->lookup( - S.Context.DeclarationNames.getCXXOperatorName(OO_Arrow)); - if (Res2.empty()) + const RecordDecl *Record = RT->getDecl(); + bool foundStarOperator = IsOverloadedOperatorPresent(Record, OO_Star); + bool foundArrowOperator = IsOverloadedOperatorPresent(Record, OO_Arrow); + if (foundStarOperator && foundArrowOperator) + return true; + + const CXXRecordDecl *CXXRecord = dyn_cast(Record); + if (!CXXRecord) return false; - return true; + for (auto BaseSpecifier : CXXRecord->bases()) { + if (!foundStarOperator) + foundStarOperator = IsOverloadedOperatorPresent( + BaseSpecifier.getType()->getAsRecordDecl(), OO_Star); + if (!foundArrowOperator) + foundArrowOperator = IsOverloadedOperatorPresent( + BaseSpecifier.getType()->getAsRecordDecl(), OO_Arrow); + } + + if (foundStarOperator && foundArrowOperator) + return true; + + return false; } /// Check if passed in Decl is a pointer type. diff --git a/test/SemaCXX/warn-thread-safety-analysis.cpp b/test/SemaCXX/warn-thread-safety-analysis.cpp index 0be2668a48..057fd17608 100644 --- a/test/SemaCXX/warn-thread-safety-analysis.cpp +++ b/test/SemaCXX/warn-thread-safety-analysis.cpp @@ -5481,3 +5481,98 @@ void f() { int &i = i; // expected-warning {{reference 'i' is not yet bound to a value when used within its own initialization}} } } + +namespace Derived_Smart_Pointer { +template +class SmartPtr_Derived : public SmartPtr {}; + +class Foo { +public: + SmartPtr_Derived mu_; + int a GUARDED_BY(mu_); + int b GUARDED_BY(mu_.get()); + int c GUARDED_BY(*mu_); + + void Lock() EXCLUSIVE_LOCK_FUNCTION(mu_); + void Unlock() UNLOCK_FUNCTION(mu_); + + void test0() { + a = 1; // expected-warning {{writing variable 'a' requires holding mutex 'mu_' exclusively}} + b = 1; // expected-warning {{writing variable 'b' requires holding mutex 'mu_' exclusively}} + c = 1; // expected-warning {{writing variable 'c' requires holding mutex 'mu_' exclusively}} + } + + void test1() { + Lock(); + a = 1; + b = 1; + c = 1; + Unlock(); + } +}; + +class Bar { + SmartPtr_Derived foo; + + void test0() { + foo->a = 1; // expected-warning {{writing variable 'a' requires holding mutex 'foo->mu_' exclusively}} + (*foo).b = 1; // expected-warning {{writing variable 'b' requires holding mutex 'foo->mu_' exclusively}} + foo.get()->c = 1; // expected-warning {{writing variable 'c' requires holding mutex 'foo->mu_' exclusively}} + } + + void test1() { + foo->Lock(); + foo->a = 1; + foo->Unlock(); + + foo->mu_->Lock(); + foo->b = 1; + foo->mu_->Unlock(); + + MutexLock lock(foo->mu_.get()); + foo->c = 1; + } +}; + +class PointerGuard { + Mutex mu1; + Mutex mu2; + SmartPtr_Derived i GUARDED_BY(mu1) PT_GUARDED_BY(mu2); + + void test0() { + i.get(); // expected-warning {{reading variable 'i' requires holding mutex 'mu1'}} + *i = 2; // expected-warning {{reading variable 'i' requires holding mutex 'mu1'}} \ + // expected-warning {{reading the value pointed to by 'i' requires holding mutex 'mu2'}} + + } + + void test1() { + mu1.Lock(); + + i.get(); + *i = 2; // expected-warning {{reading the value pointed to by 'i' requires holding mutex 'mu2'}} + + mu1.Unlock(); + } + + void test2() { + mu2.Lock(); + + i.get(); // expected-warning {{reading variable 'i' requires holding mutex 'mu1'}} + *i = 2; // expected-warning {{reading variable 'i' requires holding mutex 'mu1'}} + + mu2.Unlock(); + } + + void test3() { + mu1.Lock(); + mu2.Lock(); + + i.get(); + *i = 2; + + mu2.Unlock(); + mu1.Unlock(); + } +}; +}