[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