Skip to content

Commit

Permalink
[PCH] Reading expressions from attributes should be done using ReadEx…
Browse files Browse the repository at this point in the history
…pr(),

not ReadExpr().

Also add a test case making sure the thread safety attributes work as expected
when they come from a PCH.

Fixes rdar://12584141 & http://llvm.org/PR13982

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@168017 91177308-0d34-0410-b5e6-96231b3b80d8
  • Loading branch information
akyrtzi committed Nov 15, 2012
1 parent 5b64e77 commit 350aea7
Show file tree
Hide file tree
Showing 2 changed files with 318 additions and 1 deletion.
317 changes: 317 additions & 0 deletions 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 T>
class SmartPtr {
public:
SmartPtr(T* p) : ptr_(p) { }
SmartPtr(const SmartPtr<T>& 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
2 changes: 1 addition & 1 deletion utils/TableGen/ClangAttrEmitter.cpp
Expand Up @@ -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++]");
Expand Down

0 comments on commit 350aea7

Please sign in to comment.