This patch updates the thread safety analysis to properly substitute
call arguments for function parameters in mutex expressions.  For
example:

  void foo(MyData* d) __attribute__((exclusive_locks_required(d->mu))) { ... }
  ...
  foo(mydat);  // lock required should be mydat->mu.

The patch is broken into two parts.  The first part is a refactoring
patch, which makes it easier to write the second part, which
implements the actual functionality.

  http://codereview.appspot.com/5001048/

  -DeLesley

-- 
DeLesley Hutchins | Software Engineer | [email protected] | 505-206-0315
From 7fc2567457584127ef4b91f50d03e1849a4259a5 Mon Sep 17 00:00:00 2001
From: DeLesley Hutchins <[email protected]>
Date: Wed, 14 Sep 2011 09:08:37 -0700
Subject: [PATCH] Substitute for arguments in method calls -- refactoring

---
 lib/Analysis/ThreadSafety.cpp |  114 +++++++++++++++++++++++-----------------
 1 files changed, 65 insertions(+), 49 deletions(-)

diff --git a/lib/Analysis/ThreadSafety.cpp b/lib/Analysis/ThreadSafety.cpp
index fe9c5de..26861ae 100644
--- a/lib/Analysis/ThreadSafety.cpp
+++ b/lib/Analysis/ThreadSafety.cpp
@@ -36,14 +36,6 @@
 using namespace clang;
 using namespace thread_safety;
 
-// Helper functions
-static Expr *getParent(Expr *Exp) {
-  if (MemberExpr *ME = dyn_cast<MemberExpr>(Exp))
-    return ME->getBase();
-  if (CXXMemberCallExpr *CE = dyn_cast<CXXMemberCallExpr>(Exp))
-    return CE->getImplicitObjectArgument();
-  return 0;
-}
 
 namespace {
 /// \brief Implements a set of CFGBlocks using a BitVector.
@@ -154,32 +146,54 @@ public:
 /// foo(MyL);  // requires lock MyL->Mu to be held
 class MutexID {
   SmallVector<NamedDecl*, 2> DeclSeq;
-  ThreadSafetyHandler &Handler;
 
   /// Build a Decl sequence representing the lock from the given expression.
   /// Recursive function that bottoms out when the final DeclRefExpr is reached.
-  void buildMutexID(Expr *Exp, Expr *Parent) {
+  void buildMutexID(ThreadSafetyHandler &Handler, Expr *Exp, Expr *Parent) {
     if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Exp)) {
       NamedDecl *ND = cast<NamedDecl>(DRE->getDecl()->getCanonicalDecl());
       DeclSeq.push_back(ND);
     } else if (MemberExpr *ME = dyn_cast<MemberExpr>(Exp)) {
       NamedDecl *ND = ME->getMemberDecl();
       DeclSeq.push_back(ND);
-      buildMutexID(ME->getBase(), Parent);
+      buildMutexID(Handler, ME->getBase(), Parent);
     } else if (isa<CXXThisExpr>(Exp)) {
       if (!Parent)
         return;
-      buildMutexID(Parent, 0);
+      buildMutexID(Handler, Parent, 0);
     } else if (CastExpr *CE = dyn_cast<CastExpr>(Exp))
-      buildMutexID(CE->getSubExpr(), Parent);
+      buildMutexID(Handler, CE->getSubExpr(), Parent);
     else
       Handler.handleInvalidLockExp(Exp->getExprLoc());
   }
 
+  /// \brief Construct a MutexID from an expression.
+  /// \param AttrExp The original mutex expression within an attribute
+  /// \param Exp An expression involving the Decl on which the attribute occurs.
+  /// \param Decl The function to which the lock/unlock attribute is attached.
+  void buildMutexIDFromExp(ThreadSafetyHandler &Handler, Expr* AttrExp,
+                           Expr *Exp, const NamedDecl* Dec) {
+    Expr* Parent = 0;
+
+    if (MemberExpr *ME = dyn_cast<MemberExpr>(Exp)) {
+      Parent = ME->getBase();
+    }
+    else if (CXXMemberCallExpr *CE = dyn_cast<CXXMemberCallExpr>(Exp)) {
+      Parent = CE->getImplicitObjectArgument();
+    }
+
+    // If the attribute has no arguments, then assume the argument is "this".
+    if (AttrExp == 0) {
+      buildMutexID(Handler, Parent, 0);
+      return;
+    }
+    buildMutexID(Handler, AttrExp, Parent);
+  }
+
 public:
-  MutexID(ThreadSafetyHandler &Handler, Expr *LExpr, Expr *ParentExpr)
-    : Handler(Handler) {
-    buildMutexID(LExpr, ParentExpr);
+  MutexID(ThreadSafetyHandler &Handler,
+          Expr* AttrExp, Expr *Exp, const NamedDecl* D) {
+    buildMutexIDFromExp(Handler, AttrExp, Exp, D);
     assert(!DeclSeq.empty());
   }
 
@@ -264,9 +278,8 @@ class BuildLockset : public StmtVisitor<BuildLockset> {
   Lockset::Factory &LocksetFactory;
 
   // Helper functions
-  void removeLock(SourceLocation UnlockLoc, Expr *LockExp, Expr *Parent);
-  void addLock(SourceLocation LockLoc, Expr *LockExp, Expr *Parent,
-               LockKind LK);
+  void removeLock(SourceLocation UnlockLoc, MutexID &Mutex);
+  void addLock(SourceLocation LockLoc, MutexID &Mutex, LockKind LK);
   const ValueDecl *getValueDecl(Expr *Exp);
   void warnIfMutexNotHeld (const NamedDecl *D, Expr *Exp, AccessKind AK,
                            Expr *MutexExp, ProtectedOperationKind POK);
@@ -274,7 +287,8 @@ class BuildLockset : public StmtVisitor<BuildLockset> {
   void checkDereference(Expr *Exp, AccessKind AK);
 
   template <class AttrType>
-  void addLocksToSet(LockKind LK, Attr *Attr, CXXMemberCallExpr *Exp);
+  void addLocksToSet(LockKind LK, AttrType *Attr, CXXMemberCallExpr *Exp,
+                     NamedDecl* D);
 
   /// \brief Returns true if the lockset contains a lock, regardless of whether
   /// the lock is held exclusively or shared.
@@ -321,10 +335,9 @@ public:
 /// \brief Add a new lock to the lockset, warning if the lock is already there.
 /// \param LockLoc The source location of the acquire
 /// \param LockExp The lock expression corresponding to the lock to be added
-void BuildLockset::addLock(SourceLocation LockLoc, Expr *LockExp, Expr *Parent,
+void BuildLockset::addLock(SourceLocation LockLoc, MutexID &Mutex,
                            LockKind LK) {
   // FIXME: deal with acquired before/after annotations
-  MutexID Mutex(Handler, LockExp, Parent);
   LockData NewLock(LockLoc, LK);
 
   // FIXME: Don't always warn when we have support for reentrant locks.
@@ -336,10 +349,7 @@ void BuildLockset::addLock(SourceLocation LockLoc, Expr *LockExp, Expr *Parent,
 /// \brief Remove a lock from the lockset, warning if the lock is not there.
 /// \param LockExp The lock expression corresponding to the lock to be removed
 /// \param UnlockLoc The source location of the unlock (only used in error msg)
-void BuildLockset::removeLock(SourceLocation UnlockLoc, Expr *LockExp,
-                              Expr *Parent) {
-  MutexID Mutex(Handler, LockExp, Parent);
-
+void BuildLockset::removeLock(SourceLocation UnlockLoc, MutexID &Mutex) {
   Lockset NewLSet = LocksetFactory.remove(LSet, Mutex);
   if(NewLSet == LSet)
     Handler.handleUnmatchedUnlock(Mutex.getName(), UnlockLoc);
@@ -359,13 +369,12 @@ const ValueDecl *BuildLockset::getValueDecl(Expr *Exp) {
 }
 
 /// \brief Warn if the LSet does not contain a lock sufficient to protect access
-/// of at least the passed in AccessType.
+/// of at least the passed in AccessKind.
 void BuildLockset::warnIfMutexNotHeld(const NamedDecl *D, Expr *Exp,
                                       AccessKind AK, Expr *MutexExp,
                                       ProtectedOperationKind POK) {
   LockKind LK = getLockKindFromAccessKind(AK);
-  Expr *Parent = getParent(Exp);
-  MutexID Mutex(Handler, MutexExp, Parent);
+  MutexID Mutex(Handler, MutexExp, Exp, D);
   if (!locksetContainsAtLeast(Mutex, LK))
     Handler.handleMutexNotHeld(D, POK, Mutex.getName(), LK, Exp->getExprLoc());
 }
@@ -458,22 +467,24 @@ void BuildLockset::VisitCastExpr(CastExpr *CE) {
 /// \brief This function, parameterized by an attribute type, is used to add a
 /// set of locks specified as attribute arguments to the lockset.
 template <typename AttrType>
-void BuildLockset::addLocksToSet(LockKind LK, Attr *Attr,
-                                 CXXMemberCallExpr *Exp) {
+void BuildLockset::addLocksToSet(LockKind LK, AttrType *Attr,
+                                 CXXMemberCallExpr *Exp,
+                                 NamedDecl* FunDecl) {
   typedef typename AttrType::args_iterator iterator_type;
+
   SourceLocation ExpLocation = Exp->getExprLoc();
-  Expr *Parent = Exp->getImplicitObjectArgument();
-  AttrType *SpecificAttr = cast<AttrType>(Attr);
 
-  if (SpecificAttr->args_size() == 0) {
+  if (Attr->args_size() == 0) {
     // The mutex held is the "this" object.
-    addLock(ExpLocation, Parent, Parent, LK);
+    MutexID Mu(Handler, 0, Exp, FunDecl);
+    addLock(ExpLocation, Mu, LK);
     return;
   }
 
-  for (iterator_type I = SpecificAttr->args_begin(),
-       E = SpecificAttr->args_end(); I != E; ++I)
-    addLock(ExpLocation, *I, Parent, LK);
+  for (iterator_type I=Attr->args_begin(), E=Attr->args_end(); I != E; ++I) {
+    MutexID Mu(Handler, *I, Exp, FunDecl);
+    addLock(ExpLocation, Mu, LK);
+  }
 }
 
 /// \brief When visiting CXXMemberCallExprs we need to examine the attributes on
@@ -487,11 +498,9 @@ void BuildLockset::addLocksToSet(LockKind LK, Attr *Attr,
 ///
 /// FIXME: We need to also visit CallExprs to catch/check global functions.
 void BuildLockset::VisitCXXMemberCallExpr(CXXMemberCallExpr *Exp) {
-  NamedDecl *D = dyn_cast_or_null<NamedDecl>(Exp->getCalleeDecl());
-
   SourceLocation ExpLocation = Exp->getExprLoc();
-  Expr *Parent = Exp->getImplicitObjectArgument();
 
+  NamedDecl *D = dyn_cast_or_null<NamedDecl>(Exp->getCalleeDecl());
   if(!D || !D->hasAttrs())
     return;
 
@@ -501,15 +510,19 @@ void BuildLockset::VisitCXXMemberCallExpr(CXXMemberCallExpr *Exp) {
     switch (Attr->getKind()) {
       // When we encounter an exclusive lock function, we need to add the lock
       // to our lockset with kind exclusive.
-      case attr::ExclusiveLockFunction:
-        addLocksToSet<ExclusiveLockFunctionAttr>(LK_Exclusive, Attr, Exp);
+      case attr::ExclusiveLockFunction: {
+        ExclusiveLockFunctionAttr *A = cast<ExclusiveLockFunctionAttr>(Attr);
+        addLocksToSet(LK_Exclusive, A, Exp, D);
         break;
+      }
 
       // When we encounter a shared lock function, we need to add the lock
       // to our lockset with kind shared.
-      case attr::SharedLockFunction:
-        addLocksToSet<SharedLockFunctionAttr>(LK_Shared, Attr, Exp);
+      case attr::SharedLockFunction: {
+        SharedLockFunctionAttr *A = cast<SharedLockFunctionAttr>(Attr);
+        addLocksToSet(LK_Shared, A, Exp, D);
         break;
+      }
 
       // When we encounter an unlock function, we need to remove unlocked
       // mutexes from the lockset, and flag a warning if they are not there.
@@ -517,13 +530,16 @@ void BuildLockset::VisitCXXMemberCallExpr(CXXMemberCallExpr *Exp) {
         UnlockFunctionAttr *UFAttr = cast<UnlockFunctionAttr>(Attr);
 
         if (UFAttr->args_size() == 0) { // The lock held is the "this" object.
-          removeLock(ExpLocation, Parent, Parent);
+          MutexID Mu(Handler, 0, Exp, D);
+          removeLock(ExpLocation, Mu);
           break;
         }
 
         for (UnlockFunctionAttr::args_iterator I = UFAttr->args_begin(),
-             E = UFAttr->args_end(); I != E; ++I)
-          removeLock(ExpLocation, *I, Parent);
+             E = UFAttr->args_end(); I != E; ++I) {
+          MutexID Mu(Handler, *I, Exp, D);
+          removeLock(ExpLocation, Mu);
+        }
         break;
       }
 
@@ -556,7 +572,7 @@ void BuildLockset::VisitCXXMemberCallExpr(CXXMemberCallExpr *Exp) {
         LocksExcludedAttr *LEAttr = cast<LocksExcludedAttr>(Attr);
         for (LocksExcludedAttr::args_iterator I = LEAttr->args_begin(),
             E = LEAttr->args_end(); I != E; ++I) {
-          MutexID Mutex(Handler, *I, Parent);
+          MutexID Mutex(Handler, *I, Exp, D);
           if (locksetContains(Mutex))
             Handler.handleFunExcludesLock(D->getName(), Mutex.getName(),
                                           ExpLocation);
-- 
1.7.3.1

From 90eef09604a318840c643283ee62bd6975767775 Mon Sep 17 00:00:00 2001
From: DeLesley Hutchins <[email protected]>
Date: Thu, 15 Sep 2011 09:18:17 -0700
Subject: [PATCH] Substitute for arguments in method calls -- functionality

---
 lib/Analysis/ThreadSafety.cpp                |   43 ++++++++++++++++----
 test/SemaCXX/warn-thread-safety-analysis.cpp |   55 ++++++++++++++++++++++++++
 2 files changed, 90 insertions(+), 8 deletions(-)

diff --git a/lib/Analysis/ThreadSafety.cpp b/lib/Analysis/ThreadSafety.cpp
index 26861ae..68f8422 100644
--- a/lib/Analysis/ThreadSafety.cpp
+++ b/lib/Analysis/ThreadSafety.cpp
@@ -149,22 +149,36 @@ class MutexID {
 
   /// Build a Decl sequence representing the lock from the given expression.
   /// Recursive function that bottoms out when the final DeclRefExpr is reached.
-  void buildMutexID(ThreadSafetyHandler &Handler, Expr *Exp, Expr *Parent) {
+  void buildMutexID(ThreadSafetyHandler &Handler, Expr *Exp,
+                    Expr *Parent, int NumArgs,
+                    const NamedDecl **FunArgDecls, Expr **FunArgs) {
     if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Exp)) {
+      if (FunArgDecls) {
+        // Substitute call arguments for references to function parameters
+        for (int i = 0; i < NumArgs; ++i) {
+          if (DRE->getDecl() == FunArgDecls[i]) {
+            buildMutexID(Handler, FunArgs[i], 0, 0, 0, 0);
+            return;
+          }
+        }
+      }
       NamedDecl *ND = cast<NamedDecl>(DRE->getDecl()->getCanonicalDecl());
       DeclSeq.push_back(ND);
     } else if (MemberExpr *ME = dyn_cast<MemberExpr>(Exp)) {
       NamedDecl *ND = ME->getMemberDecl();
       DeclSeq.push_back(ND);
-      buildMutexID(Handler, ME->getBase(), Parent);
+      buildMutexID(Handler, ME->getBase(), Parent,
+                   NumArgs, FunArgDecls, FunArgs);
     } else if (isa<CXXThisExpr>(Exp)) {
       if (!Parent)
         return;
-      buildMutexID(Handler, Parent, 0);
-    } else if (CastExpr *CE = dyn_cast<CastExpr>(Exp))
-      buildMutexID(Handler, CE->getSubExpr(), Parent);
-    else
+      buildMutexID(Handler, Parent, 0, 0, 0, 0);
+    } else if (CastExpr *CE = dyn_cast<CastExpr>(Exp)) {
+      buildMutexID(Handler, CE->getSubExpr(), Parent,
+                   NumArgs, FunArgDecls, FunArgs);
+    } else {
       Handler.handleInvalidLockExp(Exp->getExprLoc());
+    }
   }
 
   /// \brief Construct a MutexID from an expression.
@@ -174,20 +188,33 @@ class MutexID {
   void buildMutexIDFromExp(ThreadSafetyHandler &Handler, Expr* AttrExp,
                            Expr *Exp, const NamedDecl* Dec) {
     Expr* Parent = 0;
+    unsigned NumArgs = 0;
+    Expr **FunArgs = 0;
+    SmallVector<const NamedDecl*, 8> FunArgDecls;
 
     if (MemberExpr *ME = dyn_cast<MemberExpr>(Exp)) {
       Parent = ME->getBase();
     }
     else if (CXXMemberCallExpr *CE = dyn_cast<CXXMemberCallExpr>(Exp)) {
       Parent = CE->getImplicitObjectArgument();
+      NumArgs = CE->getNumArgs();
+      FunArgs = CE->getArgs();
     }
 
     // If the attribute has no arguments, then assume the argument is "this".
     if (AttrExp == 0) {
-      buildMutexID(Handler, Parent, 0);
+      buildMutexID(Handler, Parent, 0, 0, 0, 0);
       return;
     }
-    buildMutexID(Handler, AttrExp, Parent);
+
+    // FIXME: handle default arguments
+    if (const FunctionDecl *D = dyn_cast_or_null<FunctionDecl>(Dec)) {
+      for (unsigned i = 0, ni = D->getNumParams(); i < ni && i < NumArgs; ++i) {
+        FunArgDecls.push_back(D->getParamDecl(i));
+      }
+    }
+    buildMutexID(Handler, AttrExp, Parent,
+                 NumArgs, &FunArgDecls.front(), FunArgs);
   }
 
 public:
diff --git a/test/SemaCXX/warn-thread-safety-analysis.cpp b/test/SemaCXX/warn-thread-safety-analysis.cpp
index 85de918..57c1308 100644
--- a/test/SemaCXX/warn-thread-safety-analysis.cpp
+++ b/test/SemaCXX/warn-thread-safety-analysis.cpp
@@ -701,3 +701,58 @@ void es_bad_7() {
     // expected-warning {{cannot call function 'le_fun' while holding mutex 'sls_mu'}}
   sls_mu.Unlock();
 }
+
+
+namespace substitution_test {
+  class MyData  {
+  public:
+    Mutex mu;
+
+    void lockData()    __attribute__((exclusive_lock_function(mu)))   { }
+    void unlockData()  __attribute__((unlock_function(mu)))           { }
+
+    void doSomething() __attribute__((exclusive_locks_required(mu)))  { }
+  };
+
+
+  class DataLocker {
+  public:
+    void lockData  (MyData *d) __attribute__((exclusive_lock_function(d->mu))) { }
+    void unlockData(MyData *d) __attribute__((unlock_function(d->mu)))         { }
+  };
+
+
+  class Foo {
+  public:
+    void foo(MyData* d) __attribute__((exclusive_locks_required(d->mu))) { }
+
+    void bar1(MyData* d) {
+      d->lockData();
+      foo(d);
+      d->unlockData();
+    }
+
+    void bar2(MyData* d) {
+      DataLocker dlr;
+      dlr.lockData(d);
+      foo(d);
+      dlr.unlockData(d);
+    }
+
+    void bar3(MyData* d1, MyData* d2) {
+      DataLocker dlr;
+      dlr.lockData(d1);   // \
+        // expected-warning {{mutex 'mu' is still held at the end of function 'bar3'}}
+      dlr.unlockData(d2); // \
+        // expected-warning {{unlocking 'mu' that was not locked}}
+    }
+
+    void bar4(MyData* d1, MyData* d2) {
+      DataLocker dlr;
+      dlr.lockData(d1);
+      foo(d2); // \
+        // expected-warning {{calling function 'foo' requires exclusive lock on 'mu'}}
+      dlr.unlockData(d1);
+    }
+  };
+} // end namespace substituation_test
-- 
1.7.3.1

_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to