r214725 - Thread safety analysis: Add support for negative requirements, which are

DeLesley Hutchins delesley at google.com
Mon Aug 4 09:10:59 PDT 2014


Author: delesley
Date: Mon Aug  4 11:10:59 2014
New Revision: 214725

URL: http://llvm.org/viewvc/llvm-project?rev=214725&view=rev
Log:
Thread safety analysis:  Add support for negative requirements, which are
capability expressions of the form !expr, and denote a capability that must
not be held.

Modified:
    cfe/trunk/include/clang/Analysis/Analyses/ThreadSafety.h
    cfe/trunk/include/clang/Analysis/Analyses/ThreadSafetyCommon.h
    cfe/trunk/lib/Analysis/ThreadSafety.cpp
    cfe/trunk/lib/Analysis/ThreadSafetyCommon.cpp
    cfe/trunk/test/SemaCXX/warn-thread-safety-analysis.cpp

Modified: cfe/trunk/include/clang/Analysis/Analyses/ThreadSafety.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Analysis/Analyses/ThreadSafety.h?rev=214725&r1=214724&r2=214725&view=diff
==============================================================================
--- cfe/trunk/include/clang/Analysis/Analyses/ThreadSafety.h (original)
+++ cfe/trunk/include/clang/Analysis/Analyses/ThreadSafety.h Mon Aug  4 11:10:59 2014
@@ -38,9 +38,9 @@ enum ProtectedOperationKind {
 /// example, it is an error to write a variable protected by shared version of a
 /// mutex.
 enum LockKind {
-  LK_Shared, ///< Shared/reader lock of a mutex.
+  LK_Shared,    ///< Shared/reader lock of a mutex.
   LK_Exclusive, ///< Exclusive/writer lock of a mutex.
-  LK_Generic  ///<  Can be either Shared or Exclusive
+  LK_Generic    ///< Can be either Shared or Exclusive
 };
 
 /// This enum distinguishes between different ways to access (read or write) a

Modified: cfe/trunk/include/clang/Analysis/Analyses/ThreadSafetyCommon.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Analysis/Analyses/ThreadSafetyCommon.h?rev=214725&r1=214724&r2=214725&view=diff
==============================================================================
--- cfe/trunk/include/clang/Analysis/Analyses/ThreadSafetyCommon.h (original)
+++ cfe/trunk/include/clang/Analysis/Analyses/ThreadSafetyCommon.h Mon Aug  4 11:10:59 2014
@@ -24,16 +24,59 @@
 
 #include "clang/Analysis/Analyses/PostOrderCFGView.h"
 #include "clang/Analysis/Analyses/ThreadSafetyTIL.h"
+#include "clang/Analysis/Analyses/ThreadSafetyTraverse.h"
 #include "clang/Analysis/AnalysisContext.h"
 #include "clang/Basic/OperatorKinds.h"
 
 #include <memory>
+#include <ostream>
+#include <sstream>
 #include <vector>
 
 
 namespace clang {
 namespace threadSafety {
 
+
+// Various helper functions on til::SExpr
+namespace sx {
+
+inline bool equals(const til::SExpr *E1, const til::SExpr *E2) {
+  return til::EqualsComparator::compareExprs(E1, E2);
+}
+
+inline bool matches(const til::SExpr *E1, const til::SExpr *E2) {
+  // We treat a top-level wildcard as the "univsersal" lock.
+  // It matches everything for the purpose of checking locks, but not
+  // for unlocking them.
+  if (isa<til::Wildcard>(E1))
+    return isa<til::Wildcard>(E2);
+  if (isa<til::Wildcard>(E2))
+    return isa<til::Wildcard>(E1);
+
+  return til::MatchComparator::compareExprs(E1, E2);
+}
+
+inline bool partiallyMatches(const til::SExpr *E1, const til::SExpr *E2) {
+  auto *PE1 = dyn_cast_or_null<til::Project>(E1);
+  if (!PE1)
+    return false;
+  auto *PE2 = dyn_cast_or_null<til::Project>(E2);
+  if (!PE2)
+    return false;
+  return PE1->clangDecl() == PE2->clangDecl();
+}
+
+inline std::string toString(const til::SExpr *E) {
+  std::stringstream ss;
+  til::StdPrinter::print(E, ss);
+  return ss.str();
+}
+
+}  // end namespace sx
+
+
+
 // This class defines the interface of a clang CFG Visitor.
 // CFGWalker will invoke the following methods.
 // Note that methods are not virtual; the visitor is templatized.
@@ -206,6 +249,59 @@ private:
 };
 
 
+
+
+class CapabilityExpr {
+  // TODO: move this back into ThreadSafety.cpp
+  // This is specific to thread safety.  It is here because
+  // translateAttrExpr needs it, but that should be moved too.
+
+private:
+  const til::SExpr* CapExpr;   //< The capability expression.
+  bool Negated;                //< True if this is a negative capability
+
+public:
+  CapabilityExpr(const til::SExpr *E, bool Neg) : CapExpr(E), Negated(Neg) {}
+
+  const til::SExpr* sexpr()    const { return CapExpr; }
+  bool              negative() const { return Negated; }
+
+  CapabilityExpr operator!() const {
+    return CapabilityExpr(CapExpr, !Negated);
+  }
+
+  bool equals(const CapabilityExpr &other) const {
+    return (Negated == other.Negated) && sx::equals(CapExpr, other.CapExpr);
+  }
+
+  bool matches(const CapabilityExpr &other) const {
+    return (Negated == other.Negated) && sx::matches(CapExpr, other.CapExpr);
+  }
+
+  bool matchesUniv(const CapabilityExpr &CapE) const {
+    return isUniversal() || matches(CapE);
+  }
+
+  bool partiallyMatches(const CapabilityExpr &other) const {
+    return (Negated == other.Negated) &&
+            sx::partiallyMatches(CapExpr, other.CapExpr);
+  }
+
+  std::string toString() const {
+    if (Negated)
+      return "!" + sx::toString(CapExpr);
+    return sx::toString(CapExpr);
+  }
+
+  bool shouldIgnore() const { return CapExpr == nullptr; }
+
+  bool isInvalid() const { return sexpr() && isa<til::Undefined>(sexpr()); }
+
+  bool isUniversal() const { return sexpr() && isa<til::Wildcard>(sexpr()); }
+};
+
+
+
 // Translate clang::Expr to til::SExpr.
 class SExprBuilder {
 public:
@@ -242,10 +338,10 @@ public:
 
   // Translate a clang expression in an attribute to a til::SExpr.
   // Constructs the context from D, DeclExp, and SelfDecl.
-  til::SExpr *translateAttrExpr(const Expr *AttrExp, const NamedDecl *D,
-                                const Expr *DeclExp, VarDecl *SelfDecl=nullptr);
+  CapabilityExpr translateAttrExpr(const Expr *AttrExp, const NamedDecl *D,
+                                   const Expr *DeclExp, VarDecl *SelfD=nullptr);
 
-  til::SExpr *translateAttrExpr(const Expr *AttrExp, CallingContext *Ctx);
+  CapabilityExpr translateAttrExpr(const Expr *AttrExp, CallingContext *Ctx);
 
   // Translate a clang statement or expression to a TIL expression.
   // Also performs substitution of variables; Ctx provides the context.

Modified: cfe/trunk/lib/Analysis/ThreadSafety.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/ThreadSafety.cpp?rev=214725&r1=214724&r2=214725&view=diff
==============================================================================
--- cfe/trunk/lib/Analysis/ThreadSafety.cpp (original)
+++ cfe/trunk/lib/Analysis/ThreadSafety.cpp Mon Aug  4 11:10:59 2014
@@ -70,141 +70,65 @@ static void warnInvalidLock(ThreadSafety
 }
 
 
-// Various helper functions on til::SExpr
-namespace sx {
-
-bool isUniversal(const til::SExpr *E) {
-  return isa<til::Wildcard>(E);
-}
-
-bool equals(const til::SExpr *E1, const til::SExpr *E2) {
-  return til::EqualsComparator::compareExprs(E1, E2);
-}
-
-const til::SExpr* ignorePtrCasts(const til::SExpr *E) {
-  if (auto *CE = dyn_cast<til::Cast>(E)) {
-    if (CE->castOpcode() == til::CAST_objToPtr)
-      return CE->expr();
-  }
-  return E;
-}
-
-bool matches(const til::SExpr *E1, const til::SExpr *E2) {
-  // We treat a top-level wildcard as the "univsersal" lock.
-  // It matches everything for the purpose of checking locks, but not
-  // for unlocking them.
-  if (isa<til::Wildcard>(E1))
-    return isa<til::Wildcard>(E2);
-  if (isa<til::Wildcard>(E2))
-    return isa<til::Wildcard>(E1);
-
-  return til::MatchComparator::compareExprs(E1, E2);
-}
-
-bool partiallyMatches(const til::SExpr *E1, const til::SExpr *E2) {
-  auto *PE1 = dyn_cast_or_null<til::Project>(E1);
-  if (!PE1)
-    return false;
-  auto *PE2 = dyn_cast_or_null<til::Project>(E2);
-  if (!PE2)
-    return false;
-  return PE1->clangDecl() == PE2->clangDecl();
-}
-
-std::string toString(const til::SExpr *E) {
-  std::stringstream ss;
-  til::StdPrinter::print(E, ss);
-  return ss.str();
-}
-
-bool shouldIgnore(const til::SExpr *E) {
-  if (!E)
-    return true;
-  // Trap mutex expressions like nullptr, or 0.
-  // Any literal value is nonsense.
-  if (isa<til::Literal>(E))
-    return true;
-  return false;
-}
-
-}  // end namespace sx
-
-
-
-/// \brief A short list of SExprs
-class MutexIDList : public SmallVector<const til::SExpr*, 3> {
+/// \brief A set of CapabilityInfo objects, which are compiled from the
+/// requires attributes on a function.
+class CapExprSet : public SmallVector<CapabilityExpr, 4> {
 public:
   /// \brief Push M onto list, but discard duplicates.
-  void push_back_nodup(const til::SExpr *E) {
-    iterator It = std::find_if(begin(), end(), [=](const til::SExpr *E2) {
-      return sx::equals(E, E2);
+  void push_back_nodup(const CapabilityExpr &CapE) {
+    iterator It = std::find_if(begin(), end(),
+                               [=](const CapabilityExpr &CapE2) {
+      return CapE.equals(CapE2);
     });
     if (It == end())
-      push_back(E);
+      push_back(CapE);
   }
 };
 
-/// \brief This is a helper class that stores info about the most recent
-/// accquire of a Lock.
+
+
+/// \brief This is a helper class that stores a fact that is known at a
+/// particular point in program execution.  Currently, a fact is a capability,
+/// along with additional information, such as where it was acquired, whether
+/// it is exclusive or shared, etc.
 ///
-/// The main body of the analysis maps MutexIDs to LockDatas.
-struct LockData {
-  SourceLocation AcquireLoc;
-
-  /// \brief LKind stores whether a lock is held shared or exclusively.
-  /// Note that this analysis does not currently support either re-entrant
-  /// locking or lock "upgrading" and "downgrading" between exclusive and
-  /// shared.
-  ///
-  /// FIXME: add support for re-entrant locking and lock up/downgrading
-  LockKind LKind;
-  bool     Asserted;           // for asserted locks
-  bool     Managed;            // for ScopedLockable objects
-  const til::SExpr* UnderlyingMutex;    // for ScopedLockable objects
-
-  LockData(SourceLocation AcquireLoc, LockKind LKind, bool M=false,
-           bool Asrt=false)
-    : AcquireLoc(AcquireLoc), LKind(LKind), Asserted(Asrt), Managed(M),
-      UnderlyingMutex(nullptr)
-  {}
+/// FIXME: this analysis does not currently support either re-entrant
+/// locking or lock "upgrading" and "downgrading" between exclusive and
+/// shared.
+class FactEntry : public CapabilityExpr {
+private:
+  LockKind          LKind;            //<  exclusive or shared
+  SourceLocation    AcquireLoc;       //<  where it was acquired.
+  bool              Managed;          //<  for ScopedLockable objects
+  bool              Asserted;         //<  true if the lock was asserted
+  const til::SExpr* UnderlyingMutex;  //<  for ScopedLockable objects
 
-  LockData(SourceLocation AcquireLoc, LockKind LKind, const til::SExpr *Mu)
-    : AcquireLoc(AcquireLoc), LKind(LKind), Asserted(false), Managed(false),
-      UnderlyingMutex(Mu)
+public:
+  FactEntry(const CapabilityExpr &CE, LockKind LK, SourceLocation Loc,
+            bool Mng=false, bool Asrt=false)
+    : CapabilityExpr(CE), LKind(LK), AcquireLoc(Loc), Managed(Mng),
+      Asserted(Asrt), UnderlyingMutex(nullptr)
   {}
 
-  bool operator==(const LockData &other) const {
-    return AcquireLoc == other.AcquireLoc && LKind == other.LKind;
-  }
-
-  bool operator!=(const LockData &other) const {
-    return !(*this == other);
-  }
+  FactEntry(const CapabilityExpr &CE, LockKind LK, SourceLocation Loc,
+            const til::SExpr *Mu)
+    : CapabilityExpr(CE), LKind(LK), AcquireLoc(Loc), Managed(false),
+      Asserted(false), UnderlyingMutex(Mu)
+  {}
 
-  void Profile(llvm::FoldingSetNodeID &ID) const {
-    ID.AddInteger(AcquireLoc.getRawEncoding());
-    ID.AddInteger(LKind);
-  }
+  LockKind          kind()       const { return LKind;    }
+  SourceLocation    loc()        const { return AcquireLoc; }
+  bool              asserted()   const { return Asserted; }
+  bool              managed()    const { return Managed;  }
+  const til::SExpr* underlying() const { return UnderlyingMutex; }
 
+  // Return true if LKind >= LK, where exclusive > shared
   bool isAtLeast(LockKind LK) {
-    return (LK == LK_Shared) || (LKind == LK_Exclusive);
+    return  (LKind == LK_Exclusive) || (LK == LK_Shared);
   }
 };
 
 
-/// \brief A FactEntry stores a single fact that is known at a particular point
-/// in the program execution.  Currently, this is information regarding a lock
-/// that is held at that point.
-struct FactEntry {
-  const til::SExpr *MutID;
-  LockData LDat;
-
-  FactEntry(const til::SExpr* M, const LockData& L)
-    : MutID(M), LDat(L)
-  { }
-};
-
-
 typedef unsigned short FactID;
 
 /// \brief FactManager manages the memory for all facts that are created during
@@ -214,8 +138,8 @@ private:
   std::vector<FactEntry> Facts;
 
 public:
-  FactID newLock(const til::SExpr *M, const LockData& L) {
-    Facts.push_back(FactEntry(M, L));
+  FactID newFact(const FactEntry &Entry) {
+    Facts.push_back(Entry);
     return static_cast<unsigned short>(Facts.size() - 1);
   }
 
@@ -249,69 +173,62 @@ public:
 
   bool isEmpty() const { return FactIDs.size() == 0; }
 
-  FactID addLock(FactManager& FM, const til::SExpr *M, const LockData& L) {
-    FactID F = FM.newLock(M, L);
+  FactID addLock(FactManager& FM, const FactEntry &Entry) {
+    FactID F = FM.newFact(Entry);
     FactIDs.push_back(F);
     return F;
   }
 
-  bool removeLock(FactManager& FM, const til::SExpr *M) {
+  bool removeLock(FactManager& FM, const CapabilityExpr &CapE) {
     unsigned n = FactIDs.size();
     if (n == 0)
       return false;
 
     for (unsigned i = 0; i < n-1; ++i) {
-      if (sx::matches(FM[FactIDs[i]].MutID, M)) {
+      if (FM[FactIDs[i]].matches(CapE)) {
         FactIDs[i] = FactIDs[n-1];
         FactIDs.pop_back();
         return true;
       }
     }
-    if (sx::matches(FM[FactIDs[n-1]].MutID, M)) {
+    if (FM[FactIDs[n-1]].matches(CapE)) {
       FactIDs.pop_back();
       return true;
     }
     return false;
   }
 
-  iterator findLockIter(FactManager &FM, const til::SExpr *M) {
+  iterator findLockIter(FactManager &FM, const CapabilityExpr &CapE) {
     return std::find_if(begin(), end(), [&](FactID ID) {
-      return sx::matches(FM[ID].MutID, M);
+      return FM[ID].matches(CapE);
     });
   }
 
-  LockData *findLock(FactManager &FM, const til::SExpr *M) const {
+  FactEntry *findLock(FactManager &FM, const CapabilityExpr &CapE) const {
     auto I = std::find_if(begin(), end(), [&](FactID ID) {
-      return sx::matches(FM[ID].MutID, M);
+      return FM[ID].matches(CapE);
     });
-
-    return I != end() ? &FM[*I].LDat : nullptr;
+    return I != end() ? &FM[*I] : nullptr;
   }
 
-  LockData *findLockUniv(FactManager &FM, const til::SExpr *M) const {
+  FactEntry *findLockUniv(FactManager &FM, const CapabilityExpr &CapE) const {
     auto I = std::find_if(begin(), end(), [&](FactID ID) -> bool {
-      const til::SExpr *E = FM[ID].MutID;
-      return sx::isUniversal(E) || sx::matches(E, M);
+      return FM[ID].matchesUniv(CapE);
     });
-
-    return I != end() ? &FM[*I].LDat : nullptr;
+    return I != end() ? &FM[*I] : nullptr;
   }
 
-  FactEntry *findPartialMatch(FactManager &FM, const til::SExpr *M) const {
+  FactEntry *findPartialMatch(FactManager &FM,
+                              const CapabilityExpr &CapE) const {
     auto I = std::find_if(begin(), end(), [&](FactID ID) {
-      return sx::partiallyMatches(FM[ID].MutID, M);
+      return FM[ID].partiallyMatches(CapE);
     });
-
     return I != end() ? &FM[*I] : nullptr;
   }
 };
 
 
-/// A Lockset maps each SExpr (defined above) to information about how it has
-/// been locked.
-typedef llvm::ImmutableMap<til::SExpr*, LockData> Lockset;
 typedef llvm::ImmutableMap<const NamedDecl*, unsigned> LocalVarContext;
-
 class LocalVariableMap;
 
 /// A side (entry or exit) of a CFG node.
@@ -839,6 +756,7 @@ class ThreadSafetyAnalyzer {
   threadSafety::SExprBuilder SxBuilder;
 
   ThreadSafetyHandler       &Handler;
+  const CXXMethodDecl       *CurrentMethod;
   LocalVariableMap          LocalVarMap;
   FactManager               FactMan;
   std::vector<CFGBlockInfo> BlockInfo;
@@ -847,18 +765,17 @@ public:
   ThreadSafetyAnalyzer(ThreadSafetyHandler &H)
      : Arena(&Bpa), SxBuilder(Arena), Handler(H) {}
 
-  void addLock(FactSet &FSet, const til::SExpr *Mutex, const LockData &LDat,
-               StringRef DiagKind);
-  void removeLock(FactSet &FSet, const til::SExpr *Mutex,
+  void addLock(FactSet &FSet, const FactEntry &Entry, StringRef DiagKind);
+  void removeLock(FactSet &FSet, const CapabilityExpr &CapE,
                   SourceLocation UnlockLoc, bool FullyRemove, LockKind Kind,
                   StringRef DiagKind);
 
   template <typename AttrType>
-  void getMutexIDs(MutexIDList &Mtxs, AttrType *Attr, Expr *Exp,
+  void getMutexIDs(CapExprSet &Mtxs, AttrType *Attr, Expr *Exp,
                    const NamedDecl *D, VarDecl *SelfDecl = nullptr);
 
   template <class AttrType>
-  void getMutexIDs(MutexIDList &Mtxs, AttrType *Attr, Expr *Exp,
+  void getMutexIDs(CapExprSet &Mtxs, AttrType *Attr, Expr *Exp,
                    const NamedDecl *D,
                    const CFGBlock *PredBlock, const CFGBlock *CurrBlock,
                    Expr *BrE, bool Neg);
@@ -965,18 +882,19 @@ ClassifyDiagnostic(const AttrTy *A) {
 /// \brief Add a new lock to the lockset, warning if the lock is already there.
 /// \param Mutex -- the Mutex expression for the lock
 /// \param LDat  -- the LockData for the lock
-void ThreadSafetyAnalyzer::addLock(FactSet &FSet, const til::SExpr *Mutex,
-                                   const LockData &LDat, StringRef DiagKind) {
-  // FIXME: deal with acquired before/after annotations.
-  // FIXME: Don't always warn when we have support for reentrant locks.
-  if (sx::shouldIgnore(Mutex))
+void ThreadSafetyAnalyzer::addLock(FactSet &FSet, const FactEntry &Entry,
+                                   StringRef DiagKind) {
+  if (Entry.shouldIgnore())
     return;
 
-  if (FSet.findLock(FactMan, Mutex)) {
-    if (!LDat.Asserted)
-      Handler.handleDoubleLock(DiagKind, sx::toString(Mutex), LDat.AcquireLoc);
+
+
+  // FIXME: deal with acquired before/after annotations.
+  // FIXME: Don't always warn when we have support for reentrant locks.
+  if (!Entry.asserted() && FSet.findLock(FactMan, Entry)) {
+    Handler.handleDoubleLock(DiagKind, Entry.toString(), Entry.loc());
   } else {
-    FSet.addLock(FactMan, Mutex, LDat);
+    FSet.addLock(FactMan, Entry);
   }
 }
 
@@ -984,77 +902,78 @@ void ThreadSafetyAnalyzer::addLock(FactS
 /// \brief Remove a lock from the lockset, warning if the lock is not there.
 /// \param Mutex The lock expression corresponding to the lock to be removed
 /// \param UnlockLoc The source location of the unlock (only used in error msg)
-void ThreadSafetyAnalyzer::removeLock(FactSet &FSet, const til::SExpr *Mutex,
+void ThreadSafetyAnalyzer::removeLock(FactSet &FSet, const CapabilityExpr &Cp,
                                       SourceLocation UnlockLoc,
                                       bool FullyRemove, LockKind ReceivedKind,
                                       StringRef DiagKind) {
-  if (sx::shouldIgnore(Mutex))
+  if (Cp.shouldIgnore())
     return;
 
-  const LockData *LDat = FSet.findLock(FactMan, Mutex);
+  const FactEntry *LDat = FSet.findLock(FactMan, Cp);
   if (!LDat) {
-    Handler.handleUnmatchedUnlock(DiagKind, sx::toString(Mutex), UnlockLoc);
+    Handler.handleUnmatchedUnlock(DiagKind, Cp.toString(), UnlockLoc);
     return;
   }
 
   // Generic lock removal doesn't care about lock kind mismatches, but
   // otherwise diagnose when the lock kinds are mismatched.
-  if (ReceivedKind != LK_Generic && LDat->LKind != ReceivedKind) {
-    Handler.handleIncorrectUnlockKind(DiagKind, sx::toString(Mutex),
-                                      LDat->LKind, ReceivedKind, UnlockLoc);
+  if (ReceivedKind != LK_Generic && LDat->kind() != ReceivedKind) {
+    Handler.handleIncorrectUnlockKind(DiagKind, Cp.toString(),
+                                      LDat->kind(), ReceivedKind, UnlockLoc);
     return;
   }
 
-  if (LDat->UnderlyingMutex) {
+  if (LDat->underlying()) {
+    CapabilityExpr UnderCp(LDat->underlying(), false);
+
     // This is scoped lockable object, which manages the real mutex.
     if (FullyRemove) {
       // We're destroying the managing object.
       // Remove the underlying mutex if it exists; but don't warn.
-      if (FSet.findLock(FactMan, LDat->UnderlyingMutex))
-        FSet.removeLock(FactMan, LDat->UnderlyingMutex);
+      if (FSet.findLock(FactMan, UnderCp))
+        FSet.removeLock(FactMan, UnderCp);
     } else {
       // We're releasing the underlying mutex, but not destroying the
       // managing object.  Warn on dual release.
-      if (!FSet.findLock(FactMan, LDat->UnderlyingMutex)) {
-        Handler.handleUnmatchedUnlock(
-            DiagKind, sx::toString(LDat->UnderlyingMutex), UnlockLoc);
+      if (!FSet.findLock(FactMan, UnderCp)) {
+        Handler.handleUnmatchedUnlock(DiagKind, UnderCp.toString(), UnlockLoc);
       }
-      FSet.removeLock(FactMan, LDat->UnderlyingMutex);
+      FSet.removeLock(FactMan, UnderCp);
       return;
     }
   }
-  FSet.removeLock(FactMan, Mutex);
+  FSet.removeLock(FactMan, Cp);
 }
 
 
 /// \brief Extract the list of mutexIDs from the attribute on an expression,
 /// and push them onto Mtxs, discarding any duplicates.
 template <typename AttrType>
-void ThreadSafetyAnalyzer::getMutexIDs(MutexIDList &Mtxs, AttrType *Attr,
+void ThreadSafetyAnalyzer::getMutexIDs(CapExprSet &Mtxs, AttrType *Attr,
                                        Expr *Exp, const NamedDecl *D,
                                        VarDecl *SelfDecl) {
   if (Attr->args_size() == 0) {
     // The mutex held is the "this" object.
-    til::SExpr *Mu = SxBuilder.translateAttrExpr(nullptr, D, Exp, SelfDecl);
-    if (Mu && isa<til::Undefined>(Mu)) {
+    CapabilityExpr Cp = SxBuilder.translateAttrExpr(nullptr, D, Exp, SelfDecl);
+    if (Cp.isInvalid()) {
        warnInvalidLock(Handler, nullptr, D, Exp, ClassifyDiagnostic(Attr));
        return;
     }
     //else
-    if (Mu)
-      Mtxs.push_back_nodup(Mu);
+    if (!Cp.shouldIgnore())
+      Mtxs.push_back_nodup(Cp);
     return;
   }
 
   for (const auto *Arg : Attr->args()) {
-    til::SExpr *Mu = SxBuilder.translateAttrExpr(Arg, D, Exp, SelfDecl);
-    if (Mu && isa<til::Undefined>(Mu)) {
+    CapabilityExpr Cp = SxBuilder.translateAttrExpr(Arg, D, Exp, SelfDecl);
+    if (Cp.isInvalid()) {
        warnInvalidLock(Handler, nullptr, D, Exp, ClassifyDiagnostic(Attr));
-       return;
+       continue;
     }
     //else
-    if (Mu)
-      Mtxs.push_back_nodup(Mu);
+    if (!Cp.shouldIgnore())
+      Mtxs.push_back_nodup(Cp);
   }
 }
 
@@ -1063,7 +982,7 @@ void ThreadSafetyAnalyzer::getMutexIDs(M
 /// trylock applies to the given edge, then push them onto Mtxs, discarding
 /// any duplicates.
 template <class AttrType>
-void ThreadSafetyAnalyzer::getMutexIDs(MutexIDList &Mtxs, AttrType *Attr,
+void ThreadSafetyAnalyzer::getMutexIDs(CapExprSet &Mtxs, AttrType *Attr,
                                        Expr *Exp, const NamedDecl *D,
                                        const CFGBlock *PredBlock,
                                        const CFGBlock *CurrBlock,
@@ -1195,8 +1114,8 @@ void ThreadSafetyAnalyzer::getEdgeLockse
   if(!FunDecl || !FunDecl->hasAttrs())
     return;
 
-  MutexIDList ExclusiveLocksToAdd;
-  MutexIDList SharedLocksToAdd;
+  CapExprSet ExclusiveLocksToAdd;
+  CapExprSet SharedLocksToAdd;
 
   // If the condition is a call to a Trylock function, then grab the attributes
   for (auto *Attr : FunDecl->getAttrs()) {
@@ -1225,10 +1144,11 @@ void ThreadSafetyAnalyzer::getEdgeLockse
   // Add and remove locks.
   SourceLocation Loc = Exp->getExprLoc();
   for (const auto &ExclusiveLockToAdd : ExclusiveLocksToAdd)
-    addLock(Result, ExclusiveLockToAdd, LockData(Loc, LK_Exclusive),
+    addLock(Result, FactEntry(ExclusiveLockToAdd, LK_Exclusive, Loc),
             CapDiagKind);
   for (const auto &SharedLockToAdd : SharedLocksToAdd)
-    addLock(Result, SharedLockToAdd, LockData(Loc, LK_Shared), CapDiagKind);
+    addLock(Result, FactEntry(SharedLockToAdd, LK_Shared, Loc),
+            CapDiagKind);
 }
 
 /// \brief We use this class to visit different types of expressions in
@@ -1245,6 +1165,7 @@ class BuildLockset : public StmtVisitor<
   unsigned CtxIndex;
 
   // Helper functions
+  bool inCurrentScope(const CapabilityExpr &CapE);
 
   void warnIfMutexNotHeld(const NamedDecl *D, const Expr *Exp, AccessKind AK,
                           Expr *MutexExp, ProtectedOperationKind POK,
@@ -1274,6 +1195,19 @@ public:
   void VisitDeclStmt(DeclStmt *S);
 };
 
+
+inline bool BuildLockset::inCurrentScope(const CapabilityExpr &CapE) {
+  if (!Analyzer->CurrentMethod)
+      return false;
+  if (auto *P = dyn_cast_or_null<til::Project>(CapE.sexpr())) {
+    auto *VD = P->clangDecl();
+    if (VD)
+      return VD->getDeclContext() == Analyzer->CurrentMethod->getDeclContext();
+  }
+  return false;
+}
+
+
 /// \brief Warn if the LSet does not contain a lock sufficient to protect access
 /// of at least the passed in AccessKind.
 void BuildLockset::warnIfMutexNotHeld(const NamedDecl *D, const Expr *Exp,
@@ -1282,59 +1216,83 @@ void BuildLockset::warnIfMutexNotHeld(co
                                       StringRef DiagKind) {
   LockKind LK = getLockKindFromAccessKind(AK);
 
-  til::SExpr *Mutex = Analyzer->SxBuilder.translateAttrExpr(MutexExp, D, Exp);
-  if (!Mutex) {
-    // TODO: invalid locks?
-    // warnInvalidLock(Analyzer->Handler, MutexExp, D, Exp, DiagKind);
+  CapabilityExpr Cp = Analyzer->SxBuilder.translateAttrExpr(MutexExp, D, Exp);
+  if (Cp.isInvalid()) {
+    warnInvalidLock(Analyzer->Handler, MutexExp, D, Exp, DiagKind);
     return;
-  } else if (sx::shouldIgnore(Mutex)) {
+  } else if (Cp.shouldIgnore()) {
     return;
   }
 
-  LockData* LDat = FSet.findLockUniv(Analyzer->FactMan, Mutex);
+  if (Cp.negative()) {
+    // Negative capabilities act like locks excluded
+    FactEntry *LDat = FSet.findLock(Analyzer->FactMan, !Cp);
+    if (LDat) {
+      Analyzer->Handler.handleFunExcludesLock(
+          DiagKind, D->getNameAsString(), (!Cp).toString(), Exp->getExprLoc());
+      return;
+    }
+
+    // If this does not refer to a negative capability in the same class,
+    // then stop here.
+    if (!inCurrentScope(Cp))
+      return;
+
+    // Otherwise the negative requirement must be propagated to the caller.
+    LDat = FSet.findLock(Analyzer->FactMan, Cp);
+    if (!LDat) {
+      Analyzer->Handler.handleMutexNotHeld("", D, POK, Cp.toString(),
+                                           LK_Shared, Exp->getExprLoc());
+    }
+    return;
+  }
+
+  FactEntry* LDat = FSet.findLockUniv(Analyzer->FactMan, Cp);
   bool NoError = true;
   if (!LDat) {
     // No exact match found.  Look for a partial match.
-    FactEntry* FEntry = FSet.findPartialMatch(Analyzer->FactMan, Mutex);
-    if (FEntry) {
+    LDat = FSet.findPartialMatch(Analyzer->FactMan, Cp);
+    if (LDat) {
       // Warn that there's no precise match.
-      LDat = &FEntry->LDat;
-      std::string PartMatchStr = sx::toString(FEntry->MutID);
+      std::string PartMatchStr = LDat->toString();
       StringRef   PartMatchName(PartMatchStr);
       Analyzer->Handler.handleMutexNotHeld(DiagKind, D, POK,
-                                           sx::toString(Mutex),
+                                           Cp.toString(),
                                            LK, Exp->getExprLoc(),
                                            &PartMatchName);
     } else {
       // Warn that there's no match at all.
       Analyzer->Handler.handleMutexNotHeld(DiagKind, D, POK,
-                                           sx::toString(Mutex),
+                                           Cp.toString(),
                                            LK, Exp->getExprLoc());
     }
     NoError = false;
   }
   // Make sure the mutex we found is the right kind.
-  if (NoError && LDat && !LDat->isAtLeast(LK))
+  if (NoError && LDat && !LDat->isAtLeast(LK)) {
     Analyzer->Handler.handleMutexNotHeld(DiagKind, D, POK,
-                                         sx::toString(Mutex),
+                                         Cp.toString(),
                                          LK, Exp->getExprLoc());
+  }
 }
 
 /// \brief Warn if the LSet contains the given lock.
 void BuildLockset::warnIfMutexHeld(const NamedDecl *D, const Expr *Exp,
                                    Expr *MutexExp,
                                    StringRef DiagKind) {
-  til::SExpr *Mutex = Analyzer->SxBuilder.translateAttrExpr(MutexExp, D, Exp);
-  if (!Mutex) {
-    // TODO: invalid locks?
-    // warnInvalidLock(Analyzer->Handler, MutexExp, D, Exp, DiagKind);
+  CapabilityExpr Cp = Analyzer->SxBuilder.translateAttrExpr(MutexExp, D, Exp);
+  if (Cp.isInvalid()) {
+    warnInvalidLock(Analyzer->Handler, MutexExp, D, Exp, DiagKind);
+    return;
+  } else if (Cp.shouldIgnore()) {
     return;
   }
 
-  LockData* LDat = FSet.findLock(Analyzer->FactMan, Mutex);
-  if (LDat)
+  FactEntry* LDat = FSet.findLock(Analyzer->FactMan, Cp);
+  if (LDat) {
     Analyzer->Handler.handleFunExcludesLock(
-        DiagKind, D->getNameAsString(), sx::toString(Mutex), Exp->getExprLoc());
+        DiagKind, D->getNameAsString(), Cp.toString(), Exp->getExprLoc());
+  }
 }
 
 /// \brief Checks guarded_by and pt_guarded_by attributes.
@@ -1423,8 +1381,8 @@ void BuildLockset::checkPtAccess(const E
 void BuildLockset::handleCall(Expr *Exp, const NamedDecl *D, VarDecl *VD) {
   SourceLocation Loc = Exp->getExprLoc();
   const AttrVec &ArgAttrs = D->getAttrs();
-  MutexIDList ExclusiveLocksToAdd, SharedLocksToAdd;
-  MutexIDList ExclusiveLocksToRemove, SharedLocksToRemove, GenericLocksToRemove;
+  CapExprSet ExclusiveLocksToAdd, SharedLocksToAdd;
+  CapExprSet ExclusiveLocksToRemove, SharedLocksToRemove, GenericLocksToRemove;
   StringRef CapDiagKind = "mutex";
 
   for(unsigned i = 0; i < ArgAttrs.size(); ++i) {
@@ -1448,22 +1406,22 @@ void BuildLockset::handleCall(Expr *Exp,
       case attr::AssertExclusiveLock: {
         AssertExclusiveLockAttr *A = cast<AssertExclusiveLockAttr>(At);
 
-        MutexIDList AssertLocks;
+        CapExprSet AssertLocks;
         Analyzer->getMutexIDs(AssertLocks, A, Exp, D, VD);
         for (const auto &AssertLock : AssertLocks)
-          Analyzer->addLock(FSet, AssertLock,
-                            LockData(Loc, LK_Exclusive, false, true),
+          Analyzer->addLock(FSet, FactEntry(AssertLock, LK_Exclusive, Loc,
+                                            false, true),
                             ClassifyDiagnostic(A));
         break;
       }
       case attr::AssertSharedLock: {
         AssertSharedLockAttr *A = cast<AssertSharedLockAttr>(At);
 
-        MutexIDList AssertLocks;
+        CapExprSet AssertLocks;
         Analyzer->getMutexIDs(AssertLocks, A, Exp, D, VD);
         for (const auto &AssertLock : AssertLocks)
-          Analyzer->addLock(FSet, AssertLock,
-                            LockData(Loc, LK_Shared, false, true),
+          Analyzer->addLock(FSet, FactEntry(AssertLock, LK_Shared, Loc,
+                                            false, true),
                             ClassifyDiagnostic(A));
         break;
       }
@@ -1516,29 +1474,30 @@ void BuildLockset::handleCall(Expr *Exp,
 
   // Add locks.
   for (const auto &M : ExclusiveLocksToAdd)
-    Analyzer->addLock(FSet, M, LockData(Loc, LK_Exclusive, isScopedVar),
+    Analyzer->addLock(FSet, FactEntry(M, LK_Exclusive, Loc, isScopedVar),
                       CapDiagKind);
   for (const auto &M : SharedLocksToAdd)
-    Analyzer->addLock(FSet, M, LockData(Loc, LK_Shared, isScopedVar),
+    Analyzer->addLock(FSet, FactEntry(M, LK_Shared, Loc, isScopedVar),
                       CapDiagKind);
 
   // Add the managing object as a dummy mutex, mapped to the underlying mutex.
-  // FIXME -- this doesn't work if we acquire multiple locks.
+  // FIXME: this doesn't work if we acquire multiple locks.
   if (isScopedVar) {
     SourceLocation MLoc = VD->getLocation();
     DeclRefExpr DRE(VD, false, VD->getType(), VK_LValue, VD->getLocation());
-    til::SExpr *SMutex = Analyzer->SxBuilder.translateAttrExpr(&DRE, nullptr);
+    // FIXME: does this store a pointer to DRE?
+    CapabilityExpr Scp = Analyzer->SxBuilder.translateAttrExpr(&DRE, nullptr);
 
     for (const auto &M : ExclusiveLocksToAdd)
-      Analyzer->addLock(FSet, SMutex, LockData(MLoc, LK_Exclusive, M),
+      Analyzer->addLock(FSet, FactEntry(Scp, LK_Exclusive, MLoc, M.sexpr()),
                         CapDiagKind);
     for (const auto &M : SharedLocksToAdd)
-      Analyzer->addLock(FSet, SMutex, LockData(MLoc, LK_Shared, M),
+      Analyzer->addLock(FSet, FactEntry(Scp, LK_Shared, MLoc, M.sexpr()),
                         CapDiagKind);
 
     // handle corner case where the underlying mutex is invalid
     if (ExclusiveLocksToAdd.size() == 0 && SharedLocksToAdd.size() == 0) {
-      Analyzer->addLock(FSet, SMutex, LockData(MLoc, LK_Exclusive),
+      Analyzer->addLock(FSet, FactEntry(Scp, LK_Exclusive, MLoc),
                         CapDiagKind);
     }
   }
@@ -1702,64 +1661,68 @@ void ThreadSafetyAnalyzer::intersectAndW
 
   // Find locks in FSet2 that conflict or are not in FSet1, and warn.
   for (const auto &Fact : FSet2) {
-    const til::SExpr *FSet2Mutex = FactMan[Fact].MutID;
-    const LockData &LDat2 = FactMan[Fact].LDat;
-    FactSet::iterator I1 = FSet1.findLockIter(FactMan, FSet2Mutex);
-
-    if (I1 != FSet1.end()) {
-      const LockData* LDat1 = &FactMan[*I1].LDat;
-      if (LDat1->LKind != LDat2.LKind) {
-        Handler.handleExclusiveAndShared("mutex", sx::toString(FSet2Mutex),
-                                         LDat2.AcquireLoc, LDat1->AcquireLoc);
-        if (Modify && LDat1->LKind != LK_Exclusive) {
+    const FactEntry *LDat1 = nullptr;
+    const FactEntry *LDat2 = &FactMan[Fact];
+    FactSet::iterator Iter1  = FSet1.findLockIter(FactMan, *LDat2);
+    if (Iter1 != FSet1.end()) LDat1 = &FactMan[*Iter1];
+
+    if (LDat1) {
+      if (LDat1->kind() != LDat2->kind()) {
+        Handler.handleExclusiveAndShared("mutex", LDat2->toString(),
+                                         LDat2->loc(), LDat1->loc());
+        if (Modify && LDat1->kind() != LK_Exclusive) {
           // Take the exclusive lock, which is the one in FSet2.
-          *I1 = Fact;
+          *Iter1 = Fact;
         }
       }
-      else if (LDat1->Asserted && !LDat2.Asserted) {
+      else if (Modify && LDat1->asserted() && !LDat2->asserted()) {
         // The non-asserted lock in FSet2 is the one we want to track.
-        *I1 = Fact;
+        *Iter1 = Fact;
       }
     } else {
-      if (LDat2.UnderlyingMutex) {
-        if (FSet2.findLock(FactMan, LDat2.UnderlyingMutex)) {
+      if (LDat2->underlying()) {
+        if (FSet2.findLock(FactMan, CapabilityExpr(LDat2->underlying(),
+                                                    false))) {
           // If this is a scoped lock that manages another mutex, and if the
           // underlying mutex is still held, then warn about the underlying
           // mutex.
           Handler.handleMutexHeldEndOfScope("mutex",
-                                            sx::toString(LDat2.UnderlyingMutex),
-                                            LDat2.AcquireLoc, JoinLoc, LEK1);
+                                            sx::toString(LDat2->underlying()),
+                                            LDat2->loc(), JoinLoc, LEK1);
         }
       }
-      else if (!LDat2.Managed && !sx::isUniversal(FSet2Mutex) &&
-               !LDat2.Asserted)
-        Handler.handleMutexHeldEndOfScope("mutex", sx::toString(FSet2Mutex),
-                                          LDat2.AcquireLoc, JoinLoc, LEK1);
+      else if (!LDat2->managed() && !LDat2->asserted() &&
+               !LDat2->isUniversal()) {
+        Handler.handleMutexHeldEndOfScope("mutex", LDat2->toString(),
+                                          LDat2->loc(), JoinLoc, LEK1);
+      }
     }
   }
 
   // Find locks in FSet1 that are not in FSet2, and remove them.
   for (const auto &Fact : FSet1Orig) {
-    const til::SExpr *FSet1Mutex = FactMan[Fact].MutID;
-    const LockData &LDat1 = FactMan[Fact].LDat;
+    const FactEntry *LDat1 = &FactMan[Fact];
+    const FactEntry *LDat2 = FSet2.findLock(FactMan, *LDat1);
 
-    if (!FSet2.findLock(FactMan, FSet1Mutex)) {
-      if (LDat1.UnderlyingMutex) {
-        if (FSet1Orig.findLock(FactMan, LDat1.UnderlyingMutex)) {
+    if (!LDat2) {
+      if (LDat1->underlying()) {
+        if (FSet1Orig.findLock(FactMan, CapabilityExpr(LDat1->underlying(),
+                                                       false))) {
           // If this is a scoped lock that manages another mutex, and if the
           // underlying mutex is still held, then warn about the underlying
           // mutex.
           Handler.handleMutexHeldEndOfScope("mutex",
-                                            sx::toString(LDat1.UnderlyingMutex),
-                                            LDat1.AcquireLoc, JoinLoc, LEK1);
+                                            sx::toString(LDat1->underlying()),
+                                            LDat1->loc(), JoinLoc, LEK1);
         }
       }
-      else if (!LDat1.Managed && !sx::isUniversal(FSet1Mutex) &&
-               !LDat1.Asserted)
-        Handler.handleMutexHeldEndOfScope("mutex", sx::toString(FSet1Mutex),
-                                          LDat1.AcquireLoc, JoinLoc, LEK2);
+      else if (!LDat1->managed() && !LDat1->asserted() &&
+               !LDat1->isUniversal()) {
+        Handler.handleMutexHeldEndOfScope("mutex", LDat1->toString(),
+                                          LDat1->loc(), JoinLoc, LEK2);
+      }
       if (Modify)
-        FSet1.removeLock(FactMan, FSet1Mutex);
+        FSet1.removeLock(FactMan, *LDat1);
     }
   }
 }
@@ -1798,6 +1761,7 @@ void ThreadSafetyAnalyzer::runAnalysis(A
 
   CFG *CFGraph = walker.getGraph();
   const NamedDecl *D = walker.getDecl();
+  CurrentMethod = dyn_cast<CXXMethodDecl>(D);
 
   if (D->hasAttr<NoThreadSafetyAnalysisAttr>())
     return;
@@ -1829,9 +1793,9 @@ void ThreadSafetyAnalyzer::runAnalysis(A
   // Fill in source locations for all CFGBlocks.
   findBlockLocations(CFGraph, SortedGraph, BlockInfo);
 
-  MutexIDList ExclusiveLocksAcquired;
-  MutexIDList SharedLocksAcquired;
-  MutexIDList LocksReleased;
+  CapExprSet ExclusiveLocksAcquired;
+  CapExprSet SharedLocksAcquired;
+  CapExprSet LocksReleased;
 
   // Add locks from exclusive_locks_required and shared_locks_required
   // to initial lockset. Also turn off checking for lock and unlock functions.
@@ -1841,8 +1805,8 @@ void ThreadSafetyAnalyzer::runAnalysis(A
     FactSet &InitialLockset = BlockInfo[FirstBlock->getBlockID()].EntrySet;
     const AttrVec &ArgAttrs = D->getAttrs();
 
-    MutexIDList ExclusiveLocksToAdd;
-    MutexIDList SharedLocksToAdd;
+    CapExprSet ExclusiveLocksToAdd;
+    CapExprSet SharedLocksToAdd;
     StringRef CapDiagKind = "mutex";
 
     SourceLocation Loc = D->getLocation();
@@ -1878,11 +1842,11 @@ void ThreadSafetyAnalyzer::runAnalysis(A
     }
 
     // FIXME -- Loc can be wrong here.
-    for (const auto &ExclusiveLockToAdd : ExclusiveLocksToAdd)
-      addLock(InitialLockset, ExclusiveLockToAdd, LockData(Loc, LK_Exclusive),
+    for (const auto &Mu : ExclusiveLocksToAdd)
+      addLock(InitialLockset, FactEntry(Mu, LK_Exclusive, Loc),
               CapDiagKind);
-    for (const auto &SharedLockToAdd : SharedLocksToAdd)
-      addLock(InitialLockset, SharedLockToAdd, LockData(Loc, LK_Shared),
+    for (const auto &Mu : SharedLocksToAdd)
+      addLock(InitialLockset, FactEntry(Mu, LK_Shared, Loc),
               CapDiagKind);
   }
 
@@ -2052,11 +2016,11 @@ void ThreadSafetyAnalyzer::runAnalysis(A
   // issue the appropriate warning.
   // FIXME: the location here is not quite right.
   for (const auto &Lock : ExclusiveLocksAcquired)
-    ExpectedExitSet.addLock(FactMan, Lock,
-                            LockData(D->getLocation(), LK_Exclusive));
+    ExpectedExitSet.addLock(FactMan, FactEntry(Lock, LK_Exclusive,
+                                               D->getLocation()));
   for (const auto &Lock : SharedLocksAcquired)
-    ExpectedExitSet.addLock(FactMan, Lock,
-                            LockData(D->getLocation(), LK_Shared));
+    ExpectedExitSet.addLock(FactMan, FactEntry(Lock, LK_Shared,
+                                               D->getLocation()));
   for (const auto &Lock : LocksReleased)
     ExpectedExitSet.removeLock(FactMan, Lock);
 

Modified: cfe/trunk/lib/Analysis/ThreadSafetyCommon.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/ThreadSafetyCommon.cpp?rev=214725&r1=214724&r2=214725&view=diff
==============================================================================
--- cfe/trunk/lib/Analysis/ThreadSafetyCommon.cpp (original)
+++ cfe/trunk/lib/Analysis/ThreadSafetyCommon.cpp Mon Aug  4 11:10:59 2014
@@ -105,10 +105,10 @@ inline bool isCalleeArrow(const Expr *E)
 /// \param D       The declaration to which the attribute is attached.
 /// \param DeclExp An expression involving the Decl to which the attribute
 ///                is attached.  E.g. the call to a function.
-til::SExpr *SExprBuilder::translateAttrExpr(const Expr *AttrExp,
-                                            const NamedDecl *D,
-                                            const Expr *DeclExp,
-                                            VarDecl *SelfDecl) {
+CapabilityExpr SExprBuilder::translateAttrExpr(const Expr *AttrExp,
+                                               const NamedDecl *D,
+                                               const Expr *DeclExp,
+                                               VarDecl *SelfDecl) {
   // If we are processing a raw attribute expression, with no substitutions.
   if (!DeclExp)
     return translateAttrExpr(AttrExp, nullptr);
@@ -163,26 +163,48 @@ til::SExpr *SExprBuilder::translateAttrE
 
 /// \brief Translate a clang expression in an attribute to a til::SExpr.
 // This assumes a CallingContext has already been created.
-til::SExpr *SExprBuilder::translateAttrExpr(const Expr *AttrExp,
-                                            CallingContext *Ctx) {
-  if (const StringLiteral* SLit = dyn_cast_or_null<StringLiteral>(AttrExp)) {
+CapabilityExpr SExprBuilder::translateAttrExpr(const Expr *AttrExp,
+                                               CallingContext *Ctx) {
+  if (!AttrExp)
+    return CapabilityExpr(nullptr, false);
+
+  if (auto* SLit = dyn_cast<StringLiteral>(AttrExp)) {
     if (SLit->getString() == StringRef("*"))
       // The "*" expr is a universal lock, which essentially turns off
       // checks until it is removed from the lockset.
-      return new (Arena) til::Wildcard();
+      return CapabilityExpr(new (Arena) til::Wildcard(), false);
     else
       // Ignore other string literals for now.
-      return nullptr;
+      return CapabilityExpr(nullptr, false);
+  }
+
+  bool Neg = false;
+  if (auto *OE = dyn_cast<CXXOperatorCallExpr>(AttrExp)) {
+    if (OE->getOperator() == OO_Exclaim) {
+      Neg = true;
+      AttrExp = OE->getArg(0);
+    }
+  }
+  else if (auto *UO = dyn_cast<UnaryOperator>(AttrExp)) {
+    if (UO->getOpcode() == UO_LNot) {
+      Neg = true;
+      AttrExp = UO->getSubExpr();
+    }
   }
 
   til::SExpr *E = translate(AttrExp, Ctx);
 
+  // Trap mutex expressions like nullptr, or 0.
+  // Any literal value is nonsense.
+  if (!E || isa<til::Literal>(E))
+    return CapabilityExpr(nullptr, false);
+
   // Hack to deal with smart pointers -- strip off top-level pointer casts.
   if (auto *CE = dyn_cast_or_null<til::Cast>(E)) {
     if (CE->castOpcode() == til::CAST_objToPtr)
-      return CE->expr();
+      return CapabilityExpr(CE->expr(), Neg);
   }
-  return E;
+  return CapabilityExpr(E, Neg);
 }
 
 
@@ -357,7 +379,8 @@ til::SExpr *SExprBuilder::translateCallE
       LRCallCtx.SelfArg  = SelfE;
       LRCallCtx.NumArgs  = CE->getNumArgs();
       LRCallCtx.FunArgs  = CE->getArgs();
-      return translateAttrExpr(At->getArg(), &LRCallCtx);
+      return const_cast<til::SExpr*>(
+          translateAttrExpr(At->getArg(), &LRCallCtx).sexpr());
     }
   }
 

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=214725&r1=214724&r2=214725&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/warn-thread-safety-analysis.cpp (original)
+++ cfe/trunk/test/SemaCXX/warn-thread-safety-analysis.cpp Mon Aug  4 11:10:59 2014
@@ -36,6 +36,9 @@ class  __attribute__((lockable)) Mutex {
   bool ReaderTryLock() __attribute__((shared_trylock_function(true)));
   void LockWhen(const int &cond) __attribute__((exclusive_lock_function));
 
+  // for negative capabilities
+  const Mutex& operator!() const { return *this; }
+
   void AssertHeld()       ASSERT_EXCLUSIVE_LOCK();
   void AssertReaderHeld() ASSERT_SHARED_LOCK();
 };
@@ -4517,3 +4520,105 @@ void test(Opaque* o) {
 
 }  // end namespace ScopedLockReturnedInvalid
 
+
+namespace NegativeRequirements {
+
+class Bar {
+  Mutex mu;
+  int a GUARDED_BY(mu);
+
+public:
+  void baz() EXCLUSIVE_LOCKS_REQUIRED(!mu) {
+    mu.Lock();
+    a = 0;
+    mu.Unlock();
+  }
+};
+
+
+class Foo {
+  Mutex mu;
+  int a GUARDED_BY(mu);
+
+public:
+  void foo() {
+    mu.Lock();    // warning?  needs !mu?
+    baz();        // expected-warning {{cannot call function 'baz' while mutex 'mu' is held}}
+    bar();
+    mu.Unlock();
+  }
+
+  void bar() {
+    baz();        // expected-warning {{calling function 'baz' requires holding  '!mu'}}
+  }
+
+  void baz() EXCLUSIVE_LOCKS_REQUIRED(!mu) {
+    mu.Lock();
+    a = 0;
+    mu.Unlock();
+  }
+
+  void test() {
+    Bar b;
+    b.baz();     // no warning -- in different class.
+  }
+};
+
+}   // end namespace NegativeRequirements
+
+
+namespace NegativeThreadRoles {
+
+typedef int __attribute__((capability("role"))) ThreadRole;
+
+void acquire(ThreadRole R) __attribute__((exclusive_lock_function(R))) __attribute__((no_thread_safety_analysis)) {}
+void release(ThreadRole R) __attribute__((unlock_function(R))) __attribute__((no_thread_safety_analysis)) {}
+
+ThreadRole FlightControl, Logger;
+
+extern void enque_log_msg(const char *msg);
+void log_msg(const char *msg) {
+  enque_log_msg(msg);
+}
+
+void dispatch_log(const char *msg) __attribute__((requires_capability(!FlightControl))) {}
+void dispatch_log2(const char *msg) __attribute__((requires_capability(Logger))) {}
+
+void flight_control_entry(void) __attribute__((requires_capability(FlightControl))) {
+  dispatch_log("wrong"); /* expected-warning {{cannot call function 'dispatch_log' while mutex 'FlightControl' is held}} */
+  dispatch_log2("also wrong"); /* expected-warning {{calling function 'dispatch_log2' requires holding role 'Logger' exclusively}} */
+}
+
+void spawn_fake_flight_control_thread(void) {
+  acquire(FlightControl);
+  flight_control_entry();
+  release(FlightControl);
+}
+
+extern const char *deque_log_msg(void) __attribute__((requires_capability(Logger)));
+void logger_entry(void) __attribute__((requires_capability(Logger))) {
+  const char *msg;
+
+  while ((msg = deque_log_msg())) {
+    dispatch_log(msg);
+  }
+}
+
+void spawn_fake_logger_thread(void) {
+  acquire(Logger);
+  logger_entry();
+  release(Logger);
+}
+
+int main(void) {
+  spawn_fake_flight_control_thread();
+  spawn_fake_logger_thread();
+
+  for (;;)
+    ; /* Pretend to dispatch things. */
+
+  return 0;
+}
+
+}
+





More information about the cfe-commits mailing list