diff --git a/clang/lib/Analysis/ThreadSafety.cpp b/clang/lib/Analysis/ThreadSafety.cpp index a912aa97962bfc069207f7d089728d0a928eb777..5bf3f8c65a105343661942d453395b5179db4651 100644 --- a/clang/lib/Analysis/ThreadSafety.cpp +++ b/clang/lib/Analysis/ThreadSafety.cpp @@ -1109,6 +1109,23 @@ void ThreadSafetyAnalyzer::getMutexIDs(MutexIDList &Mtxs, AttrType *Attr, } +bool getStaticBooleanValue(Expr* E, bool& TCond) { + if (isa(E) || isa(E)) { + TCond = false; + return true; + } else if (CXXBoolLiteralExpr *BLE = dyn_cast(E)) { + TCond = BLE->getValue(); + return true; + } else if (IntegerLiteral *ILE = dyn_cast(E)) { + TCond = ILE->getValue().getBoolValue(); + return true; + } else if (ImplicitCastExpr *CE = dyn_cast(E)) { + return getStaticBooleanValue(CE->getSubExpr(), TCond); + } + return false; +} + + // If Cond can be traced back to a function call, return the call expression. // The negate variable should be called with false, and will be set to true // if the function call is negated, e.g. if (!mu.tryLock(...)) @@ -1121,6 +1138,9 @@ const CallExpr* ThreadSafetyAnalyzer::getTrylockCallExpr(const Stmt *Cond, if (const CallExpr *CallExp = dyn_cast(Cond)) { return CallExp; } + else if (const ParenExpr *PE = dyn_cast(Cond)) { + return getTrylockCallExpr(PE->getSubExpr(), C, Negate); + } else if (const ImplicitCastExpr *CE = dyn_cast(Cond)) { return getTrylockCallExpr(CE->getSubExpr(), C, Negate); } @@ -1133,9 +1153,28 @@ const CallExpr* ThreadSafetyAnalyzer::getTrylockCallExpr(const Stmt *Cond, Negate = !Negate; return getTrylockCallExpr(UOP->getSubExpr(), C, Negate); } + return 0; + } + else if (const BinaryOperator *BOP = dyn_cast(Cond)) { + if (BOP->getOpcode() == BO_EQ || BOP->getOpcode() == BO_NE) { + if (BOP->getOpcode() == BO_NE) + Negate = !Negate; + + bool TCond = false; + if (getStaticBooleanValue(BOP->getRHS(), TCond)) { + if (!TCond) Negate = !Negate; + return getTrylockCallExpr(BOP->getLHS(), C, Negate); + } + else if (getStaticBooleanValue(BOP->getLHS(), TCond)) { + if (!TCond) Negate = !Negate; + return getTrylockCallExpr(BOP->getRHS(), C, Negate); + } + return 0; + } + return 0; } // FIXME -- handle && and || as well. - return NULL; + return 0; } diff --git a/clang/test/SemaCXX/warn-thread-safety-analysis.cpp b/clang/test/SemaCXX/warn-thread-safety-analysis.cpp index 10f9ad27366941dac70042da26c1ab99fb9aa36c..9523d43b9fc0051cdebb535300e55d3008da32e8 100644 --- a/clang/test/SemaCXX/warn-thread-safety-analysis.cpp +++ b/clang/test/SemaCXX/warn-thread-safety-analysis.cpp @@ -1,5 +1,7 @@ -// RUN: %clang_cc1 -fsyntax-only -verify -Wthread-safety %s -// RUN: %clang_cc1 -fsyntax-only -verify -Wthread-safety -std=c++11 -Wc++98-compat %s +// RUN: %clang_cc1 -fsyntax-only -verify -Wthread-safety -std=c++11 %s + +// FIXME: should also run %clang_cc1 -fsyntax-only -verify -Wthread-safety -std=c++11 -Wc++98-compat %s +// FIXME: should also run %clang_cc1 -fsyntax-only -verify -Wthread-safety %s #define LOCKABLE __attribute__ ((lockable)) #define SCOPED_LOCKABLE __attribute__ ((scoped_lockable)) @@ -2932,5 +2934,128 @@ void testlots() { +namespace TryLockEqTest { + +class Foo { + Mutex mu_; + int a GUARDED_BY(mu_); + bool c; + + int tryLockMutexI() EXCLUSIVE_TRYLOCK_FUNCTION(1, mu_); + Mutex* tryLockMutexP() EXCLUSIVE_TRYLOCK_FUNCTION(1, mu_); + void unlock() UNLOCK_FUNCTION(mu_); + + void test1(); + void test2(); +}; + + +void Foo::test1() { + if (tryLockMutexP() == 0) { + a = 0; // expected-warning {{writing variable 'a' requires locking 'mu_' exclusively}} + return; + } + a = 0; + unlock(); + + if (tryLockMutexP() != 0) { + a = 0; + unlock(); + } + + if (0 != tryLockMutexP()) { + a = 0; + unlock(); + } + + if (!(tryLockMutexP() == 0)) { + a = 0; + unlock(); + } + + if (tryLockMutexI() == 0) { + a = 0; // expected-warning {{writing variable 'a' requires locking 'mu_' exclusively}} + return; + } + a = 0; + unlock(); + + if (0 == tryLockMutexI()) { + a = 0; // expected-warning {{writing variable 'a' requires locking 'mu_' exclusively}} + return; + } + a = 0; + unlock(); + + if (tryLockMutexI() == 1) { + a = 0; + unlock(); + } + + if (mu_.TryLock() == false) { + a = 0; // expected-warning {{writing variable 'a' requires locking 'mu_' exclusively}} + return; + } + a = 0; + unlock(); + + if (mu_.TryLock() == true) { + a = 0; + unlock(); + } + else { + a = 0; // expected-warning {{writing variable 'a' requires locking 'mu_' exclusively}} + } + +#if __has_feature(cxx_nullptr) + if (tryLockMutexP() == nullptr) { + a = 0; // expected-warning {{writing variable 'a' requires locking 'mu_' exclusively}} + return; + } + a = 0; + unlock(); +#endif +} + + +void Foo::test2() { +/* FIXME: these tests depend on changes to the CFG. + * + if (mu_.TryLock() && c) { + a = 0; + unlock(); + } + else return; + + if (c && mu_.TryLock()) { + a = 0; + unlock(); + } + else return; + + if (!(mu_.TryLock() && c)) + return; + a = 0; + unlock(); + + if (!(c && mu_.TryLock())) + return; + a = 0; + unlock(); + + if (!(mu_.TryLock() == 0) && c) { + a = 0; + unlock(); + } + + if (!mu_.TryLock() || c) + return; + a = 0; + unlock(); +*/ +} + + +} // end namespace TryLockEqTest