diff --git a/test/PCH/thread-safety-attrs.cpp b/test/PCH/thread-safety-attrs.cpp new file mode 100644 index 0000000000..a588c0e523 --- /dev/null +++ b/test/PCH/thread-safety-attrs.cpp @@ -0,0 +1,317 @@ +// Test this without pch. +// RUN: %clang_cc1 -include %s -fsyntax-only -verify -Wthread-safety -std=c++11 %s + +// Test with pch. +// RUN: %clang_cc1 -emit-pch -o %t %s -std=c++11 +// RUN: %clang_cc1 -include-pch %t -fsyntax-only -verify -Wthread-safety -std=c++11 %s + +#ifndef HEADER +#define HEADER + +#define LOCKABLE __attribute__ ((lockable)) +#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__))) +#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 UNLOCK_FUNCTION(...) __attribute__ ((unlock_function(__VA_ARGS__))) +#define LOCK_RETURNED(x) __attribute__ ((lock_returned(x))) +#define LOCKS_EXCLUDED(...) __attribute__ ((locks_excluded(__VA_ARGS__))) +#define EXCLUSIVE_LOCKS_REQUIRED(...) \ + __attribute__ ((exclusive_locks_required(__VA_ARGS__))) +#define SHARED_LOCKS_REQUIRED(...) \ + __attribute__ ((shared_locks_required(__VA_ARGS__))) +#define NO_THREAD_SAFETY_ANALYSIS __attribute__ ((no_thread_safety_analysis)) + + +class __attribute__((lockable)) Mutex { + public: + void Lock() __attribute__((exclusive_lock_function)); + void ReaderLock() __attribute__((shared_lock_function)); + void Unlock() __attribute__((unlock_function)); + bool TryLock() __attribute__((exclusive_trylock_function(true))); + bool ReaderTryLock() __attribute__((shared_trylock_function(true))); + void LockWhen(const int &cond) __attribute__((exclusive_lock_function)); +}; + +class __attribute__((scoped_lockable)) MutexLock { + public: + MutexLock(Mutex *mu) __attribute__((exclusive_lock_function(mu))); + ~MutexLock() __attribute__((unlock_function)); +}; + +class __attribute__((scoped_lockable)) ReaderMutexLock { + public: + ReaderMutexLock(Mutex *mu) __attribute__((exclusive_lock_function(mu))); + ~ReaderMutexLock() __attribute__((unlock_function)); +}; + +class SCOPED_LOCKABLE ReleasableMutexLock { + public: + ReleasableMutexLock(Mutex *mu) EXCLUSIVE_LOCK_FUNCTION(mu); + ~ReleasableMutexLock() UNLOCK_FUNCTION(); + + void Release() UNLOCK_FUNCTION(); +}; + + +// The universal lock, written "*", allows checking to be selectively turned +// off for a particular piece of code. +void beginNoWarnOnReads() SHARED_LOCK_FUNCTION("*"); +void endNoWarnOnReads() UNLOCK_FUNCTION("*"); +void beginNoWarnOnWrites() EXCLUSIVE_LOCK_FUNCTION("*"); +void endNoWarnOnWrites() UNLOCK_FUNCTION("*"); + + +// For testing handling of smart pointers. +template +class SmartPtr { +public: + SmartPtr(T* p) : ptr_(p) { } + SmartPtr(const SmartPtr& p) : ptr_(p.ptr_) { } + ~SmartPtr(); + + T* get() const { return ptr_; } + T* operator->() const { return ptr_; } + T& operator*() const { return *ptr_; } + +private: + T* ptr_; +}; + + +// For testing destructor calls and cleanup. +class MyString { +public: + MyString(const char* s); + ~MyString(); +}; + + + +Mutex sls_mu; + +Mutex sls_mu2 __attribute__((acquired_after(sls_mu))); +int sls_guard_var __attribute__((guarded_var)) = 0; +int sls_guardby_var __attribute__((guarded_by(sls_mu))) = 0; + +bool getBool(); + +class MutexWrapper { +public: + Mutex mu; + int x __attribute__((guarded_by(mu))); + void MyLock() __attribute__((exclusive_lock_function(mu))); +}; + +#else + +MutexWrapper sls_mw; + +void sls_fun_0() { + sls_mw.mu.Lock(); + sls_mw.x = 5; + sls_mw.mu.Unlock(); +} + +void sls_fun_2() { + sls_mu.Lock(); + int x = sls_guard_var; + sls_mu.Unlock(); +} + +void sls_fun_3() { + sls_mu.Lock(); + sls_guard_var = 2; + sls_mu.Unlock(); +} + +void sls_fun_4() { + sls_mu2.Lock(); + sls_guard_var = 2; + sls_mu2.Unlock(); +} + +void sls_fun_5() { + sls_mu.Lock(); + int x = sls_guardby_var; + sls_mu.Unlock(); +} + +void sls_fun_6() { + sls_mu.Lock(); + sls_guardby_var = 2; + sls_mu.Unlock(); +} + +void sls_fun_7() { + sls_mu.Lock(); + sls_mu2.Lock(); + sls_mu2.Unlock(); + sls_mu.Unlock(); +} + +void sls_fun_8() { + sls_mu.Lock(); + if (getBool()) + sls_mu.Unlock(); + else + sls_mu.Unlock(); +} + +void sls_fun_9() { + if (getBool()) + sls_mu.Lock(); + else + sls_mu.Lock(); + sls_mu.Unlock(); +} + +void sls_fun_good_6() { + if (getBool()) { + sls_mu.Lock(); + } else { + if (getBool()) { + getBool(); // EMPTY + } else { + getBool(); // EMPTY + } + sls_mu.Lock(); + } + sls_mu.Unlock(); +} + +void sls_fun_good_7() { + sls_mu.Lock(); + while (getBool()) { + sls_mu.Unlock(); + if (getBool()) { + if (getBool()) { + sls_mu.Lock(); + continue; + } + } + sls_mu.Lock(); + } + sls_mu.Unlock(); +} + +void sls_fun_good_8() { + sls_mw.MyLock(); + sls_mw.mu.Unlock(); +} + +void sls_fun_bad_1() { + sls_mu.Unlock(); // \ + // expected-warning{{unlocking 'sls_mu' that was not locked}} +} + +void sls_fun_bad_2() { + sls_mu.Lock(); + sls_mu.Lock(); // \ + // expected-warning{{locking 'sls_mu' that is already locked}} + sls_mu.Unlock(); +} + +void sls_fun_bad_3() { + sls_mu.Lock(); // expected-note {{mutex acquired here}} +} // expected-warning{{mutex 'sls_mu' is still locked at the end of function}} + +void sls_fun_bad_4() { + if (getBool()) + sls_mu.Lock(); // expected-note{{mutex acquired here}} + else + sls_mu2.Lock(); // expected-note{{mutex acquired here}} +} // expected-warning{{mutex 'sls_mu' is not locked on every path through here}} \ + // expected-warning{{mutex 'sls_mu2' is not locked on every path through here}} + +void sls_fun_bad_5() { + sls_mu.Lock(); // expected-note {{mutex acquired here}} + if (getBool()) + sls_mu.Unlock(); +} // expected-warning{{mutex 'sls_mu' is not locked on every path through here}} + +void sls_fun_bad_6() { + if (getBool()) { + sls_mu.Lock(); // expected-note {{mutex acquired here}} + } else { + if (getBool()) { + getBool(); // EMPTY + } else { + getBool(); // EMPTY + } + } + sls_mu.Unlock(); // \ + expected-warning{{mutex 'sls_mu' is not locked on every path through here}}\ + expected-warning{{unlocking 'sls_mu' that was not locked}} +} + +void sls_fun_bad_7() { + sls_mu.Lock(); + while (getBool()) { + sls_mu.Unlock(); + if (getBool()) { + if (getBool()) { + continue; // \ + expected-warning{{expecting mutex 'sls_mu' to be locked at start of each loop}} + } + } + sls_mu.Lock(); // expected-note {{mutex acquired here}} + } + sls_mu.Unlock(); +} + +void sls_fun_bad_8() { + sls_mu.Lock(); // expected-note{{mutex acquired here}} + + do { + sls_mu.Unlock(); // expected-warning{{expecting mutex 'sls_mu' to be locked at start of each loop}} + } while (getBool()); +} + +void sls_fun_bad_9() { + do { + sls_mu.Lock(); // \ + // expected-warning{{expecting mutex 'sls_mu' to be locked at start of each loop}} \ + // expected-note{{mutex acquired here}} + } while (getBool()); + sls_mu.Unlock(); +} + +void sls_fun_bad_10() { + sls_mu.Lock(); // expected-note 2{{mutex acquired here}} + while(getBool()) { // expected-warning{{expecting mutex 'sls_mu' to be locked at start of each loop}} + sls_mu.Unlock(); + } +} // expected-warning{{mutex 'sls_mu' is still locked at the end of function}} + +void sls_fun_bad_11() { + while (getBool()) { // \ + expected-warning{{expecting mutex 'sls_mu' to be locked at start of each loop}} + sls_mu.Lock(); // expected-note {{mutex acquired here}} + } + sls_mu.Unlock(); // \ + // expected-warning{{unlocking 'sls_mu' that was not locked}} +} + +void sls_fun_bad_12() { + sls_mu.Lock(); // expected-note {{mutex acquired here}} + while (getBool()) { + sls_mu.Unlock(); + if (getBool()) { + if (getBool()) { + break; // expected-warning{{mutex 'sls_mu' is not locked on every path through here}} + } + } + sls_mu.Lock(); + } + sls_mu.Unlock(); +} + +#endif diff --git a/utils/TableGen/ClangAttrEmitter.cpp b/utils/TableGen/ClangAttrEmitter.cpp index 521f6046cf..08f4499aeb 100644 --- a/utils/TableGen/ClangAttrEmitter.cpp +++ b/utils/TableGen/ClangAttrEmitter.cpp @@ -47,7 +47,7 @@ static std::string ReadPCHRecord(StringRef type) { .EndsWith("Decl *", "GetLocalDeclAs<" + std::string(type, 0, type.size()-1) + ">(F, Record[Idx++])") .Case("QualType", "getLocalType(F, Record[Idx++])") - .Case("Expr *", "ReadSubExpr()") + .Case("Expr *", "ReadExpr(F)") .Case("IdentifierInfo *", "GetIdentifierInfo(F, Record, Idx)") .Case("SourceLocation", "ReadSourceLocation(F, Record, Idx)") .Default("Record[Idx++]");