[cfe-commits] r161691 - in /cfe/trunk: lib/Analysis/ThreadSafety.cpp test/SemaCXX/warn-thread-safety-analysis.cpp
DeLesley Hutchins
delesley at google.com
Fri Aug 10 13:29:46 PDT 2012
Author: delesley
Date: Fri Aug 10 15:29:46 2012
New Revision: 161691
URL: http://llvm.org/viewvc/llvm-project?rev=161691&view=rev
Log:
Thread-safety-analysis: adds existential quantification over lock
expressions. The syntax &MyClass::mutex is interpreted as a
pattern that matches m->mutex for any object m of type MyClass.
Modified:
cfe/trunk/lib/Analysis/ThreadSafety.cpp
cfe/trunk/test/SemaCXX/warn-thread-safety-analysis.cpp
Modified: cfe/trunk/lib/Analysis/ThreadSafety.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/ThreadSafety.cpp?rev=161691&r1=161690&r2=161691&view=diff
==============================================================================
--- cfe/trunk/lib/Analysis/ThreadSafety.cpp (original)
+++ cfe/trunk/lib/Analysis/ThreadSafety.cpp Fri Aug 10 15:29:46 2012
@@ -71,6 +71,7 @@
private:
enum ExprOp {
EOP_Nop, //< No-op
+ EOP_Wildcard, //< Matches anything.
EOP_This, //< This keyword.
EOP_NVar, //< Named variable.
EOP_LVar, //< Local variable.
@@ -118,6 +119,7 @@
unsigned arity() const {
switch (Op) {
case EOP_Nop: return 0;
+ case EOP_Wildcard: return 0;
case EOP_NVar: return 0;
case EOP_LVar: return 0;
case EOP_This: return 0;
@@ -134,13 +136,19 @@
bool operator==(const SExprNode& Other) const {
// Ignore flags and size -- they don't matter.
- return Op == Other.Op &&
- Data == Other.Data;
+ return (Op == Other.Op &&
+ Data == Other.Data);
}
bool operator!=(const SExprNode& Other) const {
return !(*this == Other);
}
+
+ bool matches(const SExprNode& Other) const {
+ return (*this == Other) ||
+ (Op == EOP_Wildcard) ||
+ (Other.Op == EOP_Wildcard);
+ }
};
@@ -176,15 +184,16 @@
NodeVector NodeVec;
private:
- unsigned getNextIndex(unsigned i) const {
- return i + NodeVec[i].size();
- }
-
unsigned makeNop() {
NodeVec.push_back(SExprNode(EOP_Nop, 0, 0));
return NodeVec.size()-1;
}
+ unsigned makeWildcard() {
+ NodeVec.push_back(SExprNode(EOP_Wildcard, 0, 0));
+ return NodeVec.size()-1;
+ }
+
unsigned makeNamedVar(const NamedDecl *D) {
NodeVec.push_back(SExprNode(EOP_NVar, 0, D));
return NodeVec.size()-1;
@@ -365,6 +374,16 @@
return buildSExpr(UOE->getSubExpr(), CallCtx, NDeref);
}
if (UOE->getOpcode() == UO_AddrOf) {
+ if (DeclRefExpr* DRE = dyn_cast<DeclRefExpr>(UOE->getSubExpr())) {
+ if (DRE->getDecl()->isCXXInstanceMember()) {
+ // This is a pointer-to-member expression, e.g. &MyClass::mu_.
+ // We interpret this syntax specially, as a wildcard.
+ unsigned Root = makeDot(DRE->getDecl(), false);
+ makeWildcard();
+ NodeVec[Root].setSize(2);
+ return 2;
+ }
+ }
if (NDeref) --(*NDeref);
return buildSExpr(UOE->getSubExpr(), CallCtx, NDeref);
}
@@ -464,6 +483,11 @@
buildSExpr(MutexExp, &CallCtx);
}
+ /// \brief Get index of next sibling of node i.
+ unsigned getNextSibling(unsigned i) const {
+ return i + NodeVec[i].size();
+ }
+
public:
explicit SExpr(clang::Decl::EmptyShell e) { NodeVec.clear(); }
@@ -502,6 +526,21 @@
return !(*this == other);
}
+ bool matches(const SExpr &Other, unsigned i = 0, unsigned j = 0) const {
+ if (NodeVec[i].matches(Other.NodeVec[j])) {
+ unsigned n = NodeVec[i].arity();
+ bool Result = true;
+ unsigned ci = i+1; // first child of i
+ unsigned cj = j+1; // first child of j
+ for (unsigned k = 0; k < n;
+ ++k, ci=getNextSibling(ci), cj = Other.getNextSibling(cj)) {
+ Result = Result && matches(Other, ci, cj);
+ }
+ return Result;
+ }
+ return false;
+ }
+
/// \brief Pretty print a lock expression for use in error messages.
std::string toString(unsigned i = 0) const {
assert(isValid());
@@ -512,6 +551,8 @@
switch (N->kind()) {
case EOP_Nop:
return "_";
+ case EOP_Wildcard:
+ return "(?)";
case EOP_This:
return "this";
case EOP_NVar:
@@ -519,9 +560,15 @@
return N->getNamedDecl()->getNameAsString();
}
case EOP_Dot: {
+ if (NodeVec[i+1].kind() == EOP_Wildcard) {
+ std::string S = "&";
+ S += N->getNamedDecl()->getQualifiedNameAsString();
+ return S;
+ }
std::string FieldName = N->getNamedDecl()->getNameAsString();
if (NodeVec[i+1].kind() == EOP_This)
return FieldName;
+
std::string S = toString(i+1);
if (N->isArrow())
return S + "->" + FieldName;
@@ -531,8 +578,8 @@
case EOP_Call: {
std::string S = toString(i+1) + "(";
unsigned NumArgs = N->arity()-1;
- unsigned ci = getNextIndex(i+1);
- for (unsigned k=0; k<NumArgs; ++k, ci = getNextIndex(ci)) {
+ unsigned ci = getNextSibling(i+1);
+ for (unsigned k=0; k<NumArgs; ++k, ci = getNextSibling(ci)) {
S += toString(ci);
if (k+1 < NumArgs) S += ",";
}
@@ -548,8 +595,8 @@
else
S += "#(";
unsigned NumArgs = N->arity()-1;
- unsigned ci = getNextIndex(i+1);
- for (unsigned k=0; k<NumArgs; ++k, ci = getNextIndex(ci)) {
+ unsigned ci = getNextSibling(i+1);
+ for (unsigned k=0; k<NumArgs; ++k, ci = getNextSibling(ci)) {
S += toString(ci);
if (k+1 < NumArgs) S += ",";
}
@@ -576,7 +623,7 @@
return "(...)";
std::string S = "(";
unsigned ci = i+1;
- for (unsigned j = 0; j < NumChildren; ++j, ci = getNextIndex(ci)) {
+ for (unsigned j = 0; j < NumChildren; ++j, ci = getNextSibling(ci)) {
S += toString(ci);
if (j+1 < NumChildren) S += "#";
}
@@ -720,13 +767,13 @@
return false;
for (unsigned i = 0; i < n-1; ++i) {
- if (FM[FactIDs[i]].MutID == M) {
+ if (FM[FactIDs[i]].MutID.matches(M)) {
FactIDs[i] = FactIDs[n-1];
FactIDs.pop_back();
return true;
}
}
- if (FM[FactIDs[n-1]].MutID == M) {
+ if (FM[FactIDs[n-1]].MutID.matches(M)) {
FactIDs.pop_back();
return true;
}
@@ -735,7 +782,7 @@
LockData* findLock(FactManager& FM, const SExpr& M) const {
for (const_iterator I=begin(), E=end(); I != E; ++I) {
- if (FM[*I].MutID == M) return &FM[*I].LDat;
+ if (FM[*I].MutID.matches(M)) return &FM[*I].LDat;
}
return 0;
}
Modified: cfe/trunk/test/SemaCXX/warn-thread-safety-analysis.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/warn-thread-safety-analysis.cpp?rev=161691&r1=161690&r2=161691&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/warn-thread-safety-analysis.cpp (original)
+++ cfe/trunk/test/SemaCXX/warn-thread-safety-analysis.cpp Fri Aug 10 15:29:46 2012
@@ -3056,7 +3056,66 @@
*/
}
-
} // end namespace TryLockEqTest
+namespace ExistentialPatternMatching {
+
+class Graph {
+public:
+ Mutex mu_;
+};
+
+void LockAllGraphs() EXCLUSIVE_LOCK_FUNCTION(&Graph::mu_);
+void UnlockAllGraphs() UNLOCK_FUNCTION(&Graph::mu_);
+
+class Node {
+public:
+ int a GUARDED_BY(&Graph::mu_);
+
+ void foo() EXCLUSIVE_LOCKS_REQUIRED(&Graph::mu_) {
+ a = 0;
+ }
+ void foo2() LOCKS_EXCLUDED(&Graph::mu_);
+};
+
+void test() {
+ Graph g1;
+ Graph g2;
+ Node n1;
+
+ n1.a = 0; // expected-warning {{writing variable 'a' requires locking '&ExistentialPatternMatching::Graph::mu_' exclusively}}
+ n1.foo(); // expected-warning {{calling function 'foo' requires exclusive lock on '&ExistentialPatternMatching::Graph::mu_'}}
+ n1.foo2();
+
+ g1.mu_.Lock();
+ n1.a = 0;
+ n1.foo();
+ n1.foo2(); // expected-warning {{cannot call function 'foo2' while mutex '&ExistentialPatternMatching::Graph::mu_' is locked}}
+ g1.mu_.Unlock();
+
+ g2.mu_.Lock();
+ n1.a = 0;
+ n1.foo();
+ n1.foo2(); // expected-warning {{cannot call function 'foo2' while mutex '&ExistentialPatternMatching::Graph::mu_' is locked}}
+ g2.mu_.Unlock();
+
+ LockAllGraphs();
+ n1.a = 0;
+ n1.foo();
+ n1.foo2(); // expected-warning {{cannot call function 'foo2' while mutex '&ExistentialPatternMatching::Graph::mu_' is locked}}
+ UnlockAllGraphs();
+
+ LockAllGraphs();
+ g1.mu_.Unlock();
+
+ LockAllGraphs();
+ g2.mu_.Unlock();
+
+ LockAllGraphs();
+ g1.mu_.Lock(); // expected-warning {{locking 'g1.mu_' that is already locked}}
+ g1.mu_.Unlock();
+}
+
+} // end namespace ExistentialPatternMatching
+
More information about the cfe-commits
mailing list