summaryrefslogtreecommitdiff
path: root/test/SemaCXX/warn-thread-safety-analysis.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'test/SemaCXX/warn-thread-safety-analysis.cpp')
-rw-r--r--test/SemaCXX/warn-thread-safety-analysis.cpp479
1 files changed, 443 insertions, 36 deletions
diff --git a/test/SemaCXX/warn-thread-safety-analysis.cpp b/test/SemaCXX/warn-thread-safety-analysis.cpp
index 51535be97d97e..f959beb6221e2 100644
--- a/test/SemaCXX/warn-thread-safety-analysis.cpp
+++ b/test/SemaCXX/warn-thread-safety-analysis.cpp
@@ -6,42 +6,7 @@
// 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 SCOPED_LOCKABLE __attribute__((scoped_lockable))
-#define GUARDED_BY(x) __attribute__((guarded_by(x)))
-#define GUARDED_VAR __attribute__((guarded_var))
-#define PT_GUARDED_BY(x) __attribute__((pt_guarded_by(x)))
-#define PT_GUARDED_VAR __attribute__((pt_guarded_var))
-#define ACQUIRED_AFTER(...) __attribute__((acquired_after(__VA_ARGS__)))
-#define ACQUIRED_BEFORE(...) __attribute__((acquired_before(__VA_ARGS__)))
-
-#if USE_CAPABILITY
-#define LOCKABLE __attribute__((capability("mutex")))
-#define ASSERT_EXCLUSIVE_LOCK(...) __attribute__((assert_capability(__VA_ARGS__)))
-#define ASSERT_SHARED_LOCK(...) __attribute__((assert_shared_capability(__VA_ARGS__)))
-#define EXCLUSIVE_LOCK_FUNCTION(...) __attribute__((acquire_capability(__VA_ARGS__)))
-#define SHARED_LOCK_FUNCTION(...) __attribute__((acquire_shared_capability(__VA_ARGS__)))
-#define EXCLUSIVE_TRYLOCK_FUNCTION(...) __attribute__((try_acquire_capability(__VA_ARGS__)))
-#define SHARED_TRYLOCK_FUNCTION(...) __attribute__((try_acquire_shared_capability(__VA_ARGS__)))
-#define EXCLUSIVE_LOCKS_REQUIRED(...) __attribute__((requires_capability(__VA_ARGS__)))
-#define SHARED_LOCKS_REQUIRED(...) __attribute__((requires_shared_capability(__VA_ARGS__)))
-#else
-#define LOCKABLE __attribute__((lockable))
-#define ASSERT_EXCLUSIVE_LOCK(...) __attribute__((assert_exclusive_lock(__VA_ARGS__)))
-#define ASSERT_SHARED_LOCK(...) __attribute__((assert_shared_lock(__VA_ARGS__)))
-#define EXCLUSIVE_LOCK_FUNCTION(...) __attribute__((exclusive_lock_function(__VA_ARGS__)))
-#define SHARED_LOCK_FUNCTION(...) __attribute__((shared_lock_function(__VA_ARGS__)))
-#define EXCLUSIVE_TRYLOCK_FUNCTION(...) __attribute__((exclusive_trylock_function(__VA_ARGS__)))
-#define SHARED_TRYLOCK_FUNCTION(...) __attribute__((shared_trylock_function(__VA_ARGS__)))
-#define EXCLUSIVE_LOCKS_REQUIRED(...) __attribute__((exclusive_locks_required(__VA_ARGS__)))
-#define SHARED_LOCKS_REQUIRED(...) __attribute__((shared_locks_required(__VA_ARGS__)))
-#endif
-#define EXCLUSIVE_UNLOCK_FUNCTION(...) __attribute__((release_capability(__VA_ARGS__)))
-#define SHARED_UNLOCK_FUNCTION(...) __attribute__((release_shared_capability(__VA_ARGS__)))
-#define UNLOCK_FUNCTION(...) __attribute__((unlock_function(__VA_ARGS__)))
-#define LOCK_RETURNED(x) __attribute__((lock_returned(x)))
-#define LOCKS_EXCLUDED(...) __attribute__((locks_excluded(__VA_ARGS__)))
-#define NO_THREAD_SAFETY_ANALYSIS __attribute__((no_thread_safety_analysis))
-
+#include "thread-safety-annotations.h"
class LOCKABLE Mutex {
public:
@@ -1789,6 +1754,13 @@ struct TestTryLock {
mu.Unlock();
}
+ void foo2_builtin_expect() {
+ if (__builtin_expect(!mu.TryLock(), false))
+ return;
+ a = 2;
+ mu.Unlock();
+ }
+
void foo3() {
bool b = mu.TryLock();
if (b) {
@@ -1797,6 +1769,14 @@ struct TestTryLock {
}
}
+ void foo3_builtin_expect() {
+ bool b = mu.TryLock();
+ if (__builtin_expect(b, true)) {
+ a = 3;
+ mu.Unlock();
+ }
+ }
+
void foo4() {
bool b = mu.TryLock();
if (!b) return;
@@ -1893,6 +1873,23 @@ struct TestTryLock {
int i = a;
mu.Unlock();
}
+
+ // Test with conditional operator
+ void foo13() {
+ if (mu.TryLock() ? 1 : 0)
+ mu.Unlock();
+ }
+
+ void foo14() {
+ if (mu.TryLock() ? 0 : 1)
+ return;
+ mu.Unlock();
+ }
+
+ void foo15() {
+ if (mu.TryLock() ? 0 : 1) // expected-note{{mutex acquired here}}
+ mu.Unlock(); // expected-warning{{releasing mutex 'mu' that was not held}}
+ } // expected-warning{{mutex 'mu' is not held on every path through here}}
}; // end TestTrylock
} // end namespace TrylockTest
@@ -2358,6 +2355,7 @@ Foo& getBarFoo(Bar &bar, int c) { return bar.getFoo2(c); }
void test() {
Foo foo;
Foo *fooArray;
+ Foo &(*fooFuncPtr)();
Bar bar;
int a;
int b;
@@ -2394,6 +2392,10 @@ void test() {
(a > 0 ? fooArray[1] : fooArray[b]).mu_.Lock();
(a > 0 ? fooArray[1] : fooArray[b]).a = 0;
(a > 0 ? fooArray[1] : fooArray[b]).mu_.Unlock();
+
+ fooFuncPtr().mu_.Lock();
+ fooFuncPtr().a = 0;
+ fooFuncPtr().mu_.Unlock();
}
@@ -2621,6 +2623,274 @@ void Foo::test5() {
} // end namespace ReleasableScopedLock
+namespace RelockableScopedLock {
+
+class SCOPED_LOCKABLE RelockableExclusiveMutexLock {
+public:
+ RelockableExclusiveMutexLock(Mutex *mu) EXCLUSIVE_LOCK_FUNCTION(mu);
+ ~RelockableExclusiveMutexLock() EXCLUSIVE_UNLOCK_FUNCTION();
+
+ void Lock() EXCLUSIVE_LOCK_FUNCTION();
+ void Unlock() UNLOCK_FUNCTION();
+};
+
+struct SharedTraits {};
+struct ExclusiveTraits {};
+
+class SCOPED_LOCKABLE RelockableMutexLock {
+public:
+ RelockableMutexLock(Mutex *mu, SharedTraits) SHARED_LOCK_FUNCTION(mu);
+ RelockableMutexLock(Mutex *mu, ExclusiveTraits) EXCLUSIVE_LOCK_FUNCTION(mu);
+ ~RelockableMutexLock() UNLOCK_FUNCTION();
+
+ void Lock() EXCLUSIVE_LOCK_FUNCTION();
+ void Unlock() UNLOCK_FUNCTION();
+
+ void ReaderLock() SHARED_LOCK_FUNCTION();
+ void ReaderUnlock() UNLOCK_FUNCTION();
+
+ void PromoteShared() UNLOCK_FUNCTION() EXCLUSIVE_LOCK_FUNCTION();
+ void DemoteExclusive() UNLOCK_FUNCTION() SHARED_LOCK_FUNCTION();
+};
+
+Mutex mu;
+int x GUARDED_BY(mu);
+
+void print(int);
+
+void relock() {
+ RelockableExclusiveMutexLock scope(&mu);
+ x = 2;
+ scope.Unlock();
+
+ x = 3; // expected-warning {{writing variable 'x' requires holding mutex 'mu' exclusively}}
+
+ scope.Lock();
+ x = 4;
+}
+
+void relockExclusive() {
+ RelockableMutexLock scope(&mu, SharedTraits{});
+ print(x);
+ x = 2; // expected-warning {{writing variable 'x' requires holding mutex 'mu' exclusively}}
+ scope.ReaderUnlock();
+
+ print(x); // expected-warning {{reading variable 'x' requires holding mutex 'mu'}}
+
+ scope.Lock();
+ print(x);
+ x = 4;
+
+ scope.DemoteExclusive();
+ print(x);
+ x = 5; // expected-warning {{writing variable 'x' requires holding mutex 'mu' exclusively}}
+}
+
+void relockShared() {
+ RelockableMutexLock scope(&mu, ExclusiveTraits{});
+ print(x);
+ x = 2;
+ scope.Unlock();
+
+ print(x); // expected-warning {{reading variable 'x' requires holding mutex 'mu'}}
+
+ scope.ReaderLock();
+ print(x);
+ x = 4; // expected-warning {{writing variable 'x' requires holding mutex 'mu' exclusively}}
+
+ scope.PromoteShared();
+ print(x);
+ x = 5;
+}
+
+void doubleUnlock() {
+ RelockableExclusiveMutexLock scope(&mu);
+ scope.Unlock();
+ scope.Unlock(); // expected-warning {{releasing mutex 'mu' that was not held}}
+}
+
+void doubleLock1() {
+ RelockableExclusiveMutexLock scope(&mu);
+ scope.Lock(); // expected-warning {{acquiring mutex 'mu' that is already held}}
+}
+
+void doubleLock2() {
+ RelockableExclusiveMutexLock scope(&mu);
+ scope.Unlock();
+ scope.Lock();
+ scope.Lock(); // expected-warning {{acquiring mutex 'mu' that is already held}}
+}
+
+void directUnlock() {
+ RelockableExclusiveMutexLock scope(&mu);
+ mu.Unlock();
+ // Debatable that there is no warning. Currently we don't track in the scoped
+ // object whether it is active, but just check if the contained locks can be
+ // reacquired. Here they can, because mu has been unlocked manually.
+ scope.Lock();
+}
+
+void directRelock() {
+ RelockableExclusiveMutexLock scope(&mu);
+ scope.Unlock();
+ mu.Lock();
+ // Similarly debatable that there is no warning.
+ scope.Unlock();
+}
+
+// Doesn't make a lot of sense, just making sure there is no crash.
+void destructLock() {
+ RelockableExclusiveMutexLock scope(&mu);
+ scope.~RelockableExclusiveMutexLock();
+ scope.Lock(); // Should be UB, so we don't really care.
+}
+
+class SCOPED_LOCKABLE MemberLock {
+public:
+ MemberLock() EXCLUSIVE_LOCK_FUNCTION(mutex);
+ ~MemberLock() UNLOCK_FUNCTION(mutex);
+ void Lock() EXCLUSIVE_LOCK_FUNCTION(mutex);
+ Mutex mutex;
+};
+
+void relockShared2() {
+ MemberLock lock;
+ lock.Lock(); // expected-warning {{acquiring mutex 'lock.mutex' that is already held}}
+}
+
+class SCOPED_LOCKABLE WeirdScope {
+private:
+ Mutex *other;
+
+public:
+ WeirdScope(Mutex *mutex) EXCLUSIVE_LOCK_FUNCTION(mutex);
+ void unlock() EXCLUSIVE_UNLOCK_FUNCTION() EXCLUSIVE_UNLOCK_FUNCTION(other);
+ void lock() EXCLUSIVE_LOCK_FUNCTION() EXCLUSIVE_LOCK_FUNCTION(other);
+ ~WeirdScope() EXCLUSIVE_UNLOCK_FUNCTION();
+
+ void requireOther() EXCLUSIVE_LOCKS_REQUIRED(other);
+};
+
+void relockWeird() {
+ WeirdScope scope(&mu);
+ x = 1;
+ scope.unlock(); // expected-warning {{releasing mutex 'scope.other' that was not held}}
+ x = 2; // \
+ // expected-warning {{writing variable 'x' requires holding mutex 'mu' exclusively}}
+ scope.requireOther(); // \
+ // expected-warning {{calling function 'requireOther' requires holding mutex 'scope.other' exclusively}}
+ scope.lock(); // expected-note {{mutex acquired here}}
+ x = 3;
+ scope.requireOther();
+} // expected-warning {{mutex 'scope.other' is still held at the end of function}}
+
+} // end namespace RelockableScopedLock
+
+
+namespace ScopedUnlock {
+
+class SCOPED_LOCKABLE MutexUnlock {
+public:
+ MutexUnlock(Mutex *mu) EXCLUSIVE_UNLOCK_FUNCTION(mu);
+ ~MutexUnlock() EXCLUSIVE_UNLOCK_FUNCTION();
+
+ void Lock() EXCLUSIVE_UNLOCK_FUNCTION();
+ void Unlock() EXCLUSIVE_LOCK_FUNCTION();
+};
+
+class SCOPED_LOCKABLE ReaderMutexUnlock {
+public:
+ ReaderMutexUnlock(Mutex *mu) SHARED_UNLOCK_FUNCTION(mu);
+ ~ReaderMutexUnlock() EXCLUSIVE_UNLOCK_FUNCTION();
+
+ void Lock() EXCLUSIVE_UNLOCK_FUNCTION();
+ void Unlock() EXCLUSIVE_LOCK_FUNCTION();
+};
+
+Mutex mu;
+int x GUARDED_BY(mu);
+bool c;
+void print(int);
+
+void simple() EXCLUSIVE_LOCKS_REQUIRED(mu) {
+ x = 1;
+ MutexUnlock scope(&mu);
+ x = 2; // expected-warning {{writing variable 'x' requires holding mutex 'mu' exclusively}}
+}
+
+void simpleShared() SHARED_LOCKS_REQUIRED(mu) {
+ print(x);
+ ReaderMutexUnlock scope(&mu);
+ print(x); // expected-warning {{reading variable 'x' requires holding mutex 'mu'}}
+}
+
+void innerUnlock() {
+ MutexLock outer(&mu);
+ if (x == 0) {
+ MutexUnlock inner(&mu);
+ x = 1; // expected-warning {{writing variable 'x' requires holding mutex 'mu' exclusively}}
+ }
+ x = 2;
+}
+
+void innerUnlockShared() {
+ ReaderMutexLock outer(&mu);
+ if (x == 0) {
+ ReaderMutexUnlock inner(&mu);
+ print(x); // expected-warning {{reading variable 'x' requires holding mutex 'mu'}}
+ }
+ print(x);
+}
+
+void manual() EXCLUSIVE_LOCKS_REQUIRED(mu) {
+ MutexUnlock scope(&mu);
+ scope.Lock();
+ x = 2;
+ scope.Unlock();
+ x = 3; // expected-warning {{writing variable 'x' requires holding mutex 'mu' exclusively}}
+}
+
+void join() EXCLUSIVE_LOCKS_REQUIRED(mu) {
+ MutexUnlock scope(&mu);
+ if (c) {
+ scope.Lock(); // expected-note{{mutex acquired here}}
+ }
+ // expected-warning@+1{{mutex 'mu' is not held on every path through here}}
+ scope.Lock();
+}
+
+void doubleLock() EXCLUSIVE_LOCKS_REQUIRED(mu) {
+ MutexUnlock scope(&mu);
+ scope.Lock();
+ scope.Lock(); // expected-warning {{acquiring mutex 'mu' that is already held}}
+}
+
+void doubleUnlock() EXCLUSIVE_LOCKS_REQUIRED(mu) {
+ MutexUnlock scope(&mu);
+ scope.Unlock(); // expected-warning {{releasing mutex 'mu' that was not held}}
+}
+
+class SCOPED_LOCKABLE MutexLockUnlock {
+public:
+ MutexLockUnlock(Mutex *mu1, Mutex *mu2) EXCLUSIVE_UNLOCK_FUNCTION(mu1) EXCLUSIVE_LOCK_FUNCTION(mu2);
+ ~MutexLockUnlock() EXCLUSIVE_UNLOCK_FUNCTION();
+
+ void Release() EXCLUSIVE_UNLOCK_FUNCTION();
+ void Acquire() EXCLUSIVE_LOCK_FUNCTION();
+};
+
+Mutex other;
+void fn() EXCLUSIVE_LOCKS_REQUIRED(other);
+
+void lockUnlock() EXCLUSIVE_LOCKS_REQUIRED(mu) {
+ MutexLockUnlock scope(&mu, &other);
+ fn();
+ x = 1; // expected-warning {{writing variable 'x' requires holding mutex 'mu' exclusively}}
+}
+
+} // end namespace ScopedUnlock
+
+
namespace TrylockFunctionTest {
class Foo {
@@ -4078,6 +4348,14 @@ public:
mu_.Unlock();
}
+ void unlockExclusive() EXCLUSIVE_UNLOCK_FUNCTION(mu_) {
+ mu_.Unlock();
+ }
+
+ void unlockShared() SHARED_UNLOCK_FUNCTION(mu_) {
+ mu_.ReaderUnlock();
+ }
+
// Check failure to lock.
void lockBad() EXCLUSIVE_LOCK_FUNCTION(mu_) { // expected-note {{mutex acquired here}}
mu2_.Lock();
@@ -4840,6 +5118,8 @@ public:
void operator+(const Foo& f);
void operator[](const Foo& g);
+
+ void operator()();
};
template<class T>
@@ -4857,8 +5137,23 @@ void destroy(Foo&& f);
void operator/(const Foo& f, const Foo& g);
void operator*(const Foo& f, const Foo& g);
+// Test constructors.
+struct FooRead {
+ FooRead(const Foo &);
+};
+struct FooWrite {
+ FooWrite(Foo &);
+};
+// Test variadic functions
+template<typename... T>
+void copyVariadic(T...) {}
+template<typename... T>
+void writeVariadic(T&...) {}
+template<typename... T>
+void readVariadic(const T&...) {}
+void copyVariadicC(int, ...);
class Bar {
public:
@@ -4890,6 +5185,14 @@ public:
read2(10, foo); // expected-warning {{passing variable 'foo' by reference requires holding mutex 'mu'}}
destroy(mymove(foo)); // expected-warning {{passing variable 'foo' by reference requires holding mutex 'mu'}}
+ copyVariadic(foo); // expected-warning {{reading variable 'foo' requires holding mutex 'mu'}}
+ readVariadic(foo); // expected-warning {{passing variable 'foo' by reference requires holding mutex 'mu'}}
+ writeVariadic(foo); // expected-warning {{passing variable 'foo' by reference requires holding mutex 'mu'}}
+ copyVariadicC(1, foo); // expected-warning {{reading variable 'foo' requires holding mutex 'mu'}}
+
+ FooRead reader(foo); // expected-warning {{passing variable 'foo' by reference requires holding mutex 'mu'}}
+ FooWrite writer(foo); // expected-warning {{passing variable 'foo' by reference requires holding mutex 'mu'}}
+
mwrite1(foo); // expected-warning {{passing variable 'foo' by reference requires holding mutex 'mu'}}
mwrite2(10, foo); // expected-warning {{passing variable 'foo' by reference requires holding mutex 'mu'}}
mread1(foo); // expected-warning {{passing variable 'foo' by reference requires holding mutex 'mu'}}
@@ -4908,6 +5211,7 @@ public:
// expected-warning {{passing variable 'foo2' by reference requires holding mutex 'mu'}}
foo[foo2]; // expected-warning {{reading variable 'foo' requires holding mutex 'mu'}} \
// expected-warning {{passing variable 'foo2' by reference requires holding mutex 'mu'}}
+ foo(); // expected-warning {{reading variable 'foo' requires holding mutex 'mu'}}
(*this) << foo; // expected-warning {{passing variable 'foo' by reference requires holding mutex 'mu'}}
copy(*foop); // expected-warning {{reading the value pointed to by 'foop' requires holding mutex 'mu'}}
@@ -5331,3 +5635,106 @@ namespace ReturnScopedLockable {
return ptr->f();
}
}
+
+namespace PR38640 {
+void f() {
+ // Self-referencing assignment previously caused an infinite loop when thread
+ // safety analysis was enabled.
+ 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 T>
+class SmartPtr_Derived : public SmartPtr<T> {};
+
+class Foo {
+public:
+ SmartPtr_Derived<Mutex> 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> 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<int> 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();
+ }
+};
+}