r214089 - Thread Safety Analysis: Replace the old and broken SExpr with the new

DeLesley Hutchins delesley at google.com
Mon Jul 28 08:57:28 PDT 2014


Author: delesley
Date: Mon Jul 28 10:57:27 2014
New Revision: 214089

URL: http://llvm.org/viewvc/llvm-project?rev=214089&view=rev
Log:
Thread Safety Analysis:  Replace the old and broken SExpr with the new
til::SExpr.  This is a large patch, with many small changes to pretty printing
and expression lowering to make the new SExpr representation equivalent in
functionality to the old.

Modified:
    cfe/trunk/include/clang/Analysis/Analyses/ThreadSafety.h
    cfe/trunk/include/clang/Analysis/Analyses/ThreadSafetyCommon.h
    cfe/trunk/include/clang/Analysis/Analyses/ThreadSafetyTIL.h
    cfe/trunk/include/clang/Analysis/Analyses/ThreadSafetyTraverse.h
    cfe/trunk/include/clang/Analysis/Analyses/ThreadSafetyUtil.h
    cfe/trunk/lib/Analysis/ThreadSafety.cpp
    cfe/trunk/lib/Analysis/ThreadSafetyCommon.cpp
    cfe/trunk/lib/Analysis/ThreadSafetyTIL.cpp
    cfe/trunk/lib/Sema/AnalysisBasedWarnings.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=214089&r1=214088&r2=214089&view=diff
==============================================================================
--- cfe/trunk/include/clang/Analysis/Analyses/ThreadSafety.h (original)
+++ cfe/trunk/include/clang/Analysis/Analyses/ThreadSafety.h Mon Jul 28 10:57:27 2014
@@ -24,7 +24,7 @@
 #include "llvm/ADT/StringRef.h"
 
 namespace clang {
-namespace thread_safety {
+namespace threadSafety {
 
 /// This enum distinguishes between different kinds of operations that may
 /// need to be protected by locks. We use this enum in error handling.
@@ -190,5 +190,5 @@ void runThreadSafetyAnalysis(AnalysisDec
 /// of access.
 LockKind getLockKindFromAccessKind(AccessKind AK);
 
-}} // end namespace clang::thread_safety
+}} // end namespace clang::threadSafety
 #endif

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=214089&r1=214088&r2=214089&view=diff
==============================================================================
--- cfe/trunk/include/clang/Analysis/Analyses/ThreadSafetyCommon.h (original)
+++ cfe/trunk/include/clang/Analysis/Analyses/ThreadSafetyCommon.h Mon Jul 28 10:57:27 2014
@@ -219,18 +219,16 @@ public:
   /// should be evaluated; multiple calling contexts can be chained together
   /// by the lock_returned attribute.
   struct CallingContext {
+    CallingContext  *Prev;      // The previous context; or 0 if none.
     const NamedDecl *AttrDecl;  // The decl to which the attr is attached.
     const Expr *SelfArg;        // Implicit object argument -- e.g. 'this'
     unsigned NumArgs;           // Number of funArgs
     const Expr *const *FunArgs; // Function arguments
-    CallingContext *Prev;       // The previous context; or 0 if none.
     bool SelfArrow;             // is Self referred to with -> or .?
 
-    CallingContext(const NamedDecl *D = nullptr, const Expr *S = nullptr,
-                   unsigned N = 0, const Expr *const *A = nullptr,
-                   CallingContext *P = nullptr)
-        : AttrDecl(D), SelfArg(S), NumArgs(N), FunArgs(A), Prev(P),
-          SelfArrow(false)
+    CallingContext(CallingContext *P, const NamedDecl *D = nullptr)
+        : Prev(P), AttrDecl(D), SelfArg(nullptr),
+          NumArgs(0), FunArgs(nullptr), SelfArrow(false)
     {}
   };
 
@@ -242,6 +240,13 @@ public:
     SelfVar->setKind(til::Variable::VK_SFun);
   }
 
+  // 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);
+
+  til::SExpr *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.
   // Dispatches on the type of S.
@@ -262,7 +267,8 @@ private:
                                    CallingContext *Ctx) ;
   til::SExpr *translateCXXThisExpr(const CXXThisExpr *TE, CallingContext *Ctx);
   til::SExpr *translateMemberExpr(const MemberExpr *ME, CallingContext *Ctx);
-  til::SExpr *translateCallExpr(const CallExpr *CE, CallingContext *Ctx);
+  til::SExpr *translateCallExpr(const CallExpr *CE, CallingContext *Ctx,
+                                const Expr *SelfE = nullptr);
   til::SExpr *translateCXXMemberCallExpr(const CXXMemberCallExpr *ME,
                                          CallingContext *Ctx);
   til::SExpr *translateCXXOperatorCallExpr(const CXXOperatorCallExpr *OCE,
@@ -280,10 +286,8 @@ private:
   til::SExpr *translateCastExpr(const CastExpr *CE, CallingContext *Ctx);
   til::SExpr *translateArraySubscriptExpr(const ArraySubscriptExpr *E,
                                           CallingContext *Ctx);
-  til::SExpr *translateConditionalOperator(const ConditionalOperator *C,
-                                           CallingContext *Ctx);
-  til::SExpr *translateBinaryConditionalOperator(
-      const BinaryConditionalOperator *C, CallingContext *Ctx);
+  til::SExpr *translateAbstractConditionalOperator(
+      const AbstractConditionalOperator *C, CallingContext *Ctx);
 
   til::SExpr *translateDeclStmt(const DeclStmt *S, CallingContext *Ctx);
 
@@ -362,16 +366,19 @@ private:
   void mergePhiNodesBackEdge(const CFGBlock *Blk);
 
 private:
+  // Set to true when parsing capability expressions, which get translated
+  // inaccurately in order to hack around smart pointers etc.
+  static const bool CapabilityExprMode = true;
+
   til::MemRegionRef Arena;
   til::Variable *SelfVar;       // Variable to use for 'this'.  May be null.
-  til::SCFG *Scfg;
 
+  til::SCFG *Scfg;
   StatementMap SMap;                       // Map from Stmt to TIL Variables
   LVarIndexMap LVarIdxMap;                 // Indices of clang local vars.
   std::vector<til::BasicBlock *> BlockMap; // Map from clang to til BBs.
   std::vector<BlockInfo> BBInfo;           // Extra information per BB.
                                            // Indexed by clang BlockID.
-  std::unique_ptr<SExprBuilder::CallingContext> CallCtx; // Root calling context
 
   LVarDefinitionMap CurrentLVarMap;
   std::vector<til::Variable*> CurrentArguments;

Modified: cfe/trunk/include/clang/Analysis/Analyses/ThreadSafetyTIL.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Analysis/Analyses/ThreadSafetyTIL.h?rev=214089&r1=214088&r2=214089&view=diff
==============================================================================
--- cfe/trunk/include/clang/Analysis/Analyses/ThreadSafetyTIL.h (original)
+++ cfe/trunk/include/clang/Analysis/Analyses/ThreadSafetyTIL.h Mon Jul 28 10:57:27 2014
@@ -100,6 +100,7 @@ enum TIL_CastOpcode : unsigned char {
   CAST_truncNum,    // truncate precision of numeric type
   CAST_toFloat,     // convert to floating point type
   CAST_toInt,       // convert to integer type
+  CAST_objToPtr     // convert smart pointer to pointer  (C++ only)
 };
 
 const TIL_Opcode       COP_Min  = COP_Future;
@@ -405,7 +406,8 @@ public:
     return Vs.reduceVariableRef(this);
   }
 
-  template <class C> typename C::CType compare(Variable* E, C& Cmp) {
+  template <class C>
+  typename C::CType compare(const Variable* E, C& Cmp) const {
     return Cmp.compareVariableRefs(this, E);
   }
 
@@ -455,7 +457,7 @@ public:
   virtual SExpr *create() { return nullptr; }
 
   // Return the result of this future if it exists, otherwise return null.
-  SExpr *maybeGetResult() {
+  SExpr *maybeGetResult() const {
     return Result;
   }
 
@@ -478,7 +480,8 @@ public:
     return Vs.traverse(Result, Ctx);
   }
 
-  template <class C> typename C::CType compare(Future* E, C& Cmp) {
+  template <class C>
+  typename C::CType compare(const Future* E, C& Cmp) const {
     if (!Result || !E->Result)
       return Cmp.comparePointers(this, E);
     return Cmp.compare(Result, E->Result);
@@ -572,8 +575,9 @@ public:
     return Vs.reduceUndefined(*this);
   }
 
-  template <class C> typename C::CType compare(Undefined* E, C& Cmp) {
-    return Cmp.comparePointers(Cstmt, E->Cstmt);
+  template <class C>
+  typename C::CType compare(const Undefined* E, C& Cmp) const {
+    return Cmp.trueResult();
   }
 
 private:
@@ -593,7 +597,8 @@ public:
     return Vs.reduceWildcard(*this);
   }
 
-  template <class C> typename C::CType compare(Wildcard* E, C& Cmp) {
+  template <class C>
+  typename C::CType compare(const Wildcard* E, C& Cmp) const {
     return Cmp.trueResult();
   }
 };
@@ -626,9 +631,10 @@ public:
 
   template <class V> typename V::R_SExpr traverse(V &Vs, typename V::R_Ctx Ctx);
 
-  template <class C> typename C::CType compare(Literal* E, C& Cmp) {
-    // TODO -- use value, not pointer equality
-    return Cmp.comparePointers(Cexpr, E->Cexpr);
+  template <class C>
+  typename C::CType compare(const Literal* E, C& Cmp) const {
+    // TODO: defer actual comparison to LiteralT
+    return Cmp.trueResult();
   }
 
 private:
@@ -727,7 +733,8 @@ public:
     return Vs.reduceLiteralPtr(*this);
   }
 
-  template <class C> typename C::CType compare(LiteralPtr* E, C& Cmp) {
+  template <class C>
+  typename C::CType compare(const LiteralPtr* E, C& Cmp) const {
     return Cmp.comparePointers(Cvdecl, E->Cvdecl);
   }
 
@@ -769,7 +776,8 @@ public:
     return Vs.reduceFunction(*this, Nvd, E1);
   }
 
-  template <class C> typename C::CType compare(Function* E, C& Cmp) {
+  template <class C>
+  typename C::CType compare(const Function* E, C& Cmp) const {
     typename C::CType Ct =
       Cmp.compare(VarDecl->definition(), E->VarDecl->definition());
     if (Cmp.notTrue(Ct))
@@ -824,7 +832,8 @@ public:
     return Vs.reduceSFunction(*this, Nvd, E1);
   }
 
-  template <class C> typename C::CType compare(SFunction* E, C& Cmp) {
+  template <class C>
+  typename C::CType compare(const SFunction* E, C& Cmp) const {
     Cmp.enterScope(variableDecl(), E->variableDecl());
     typename C::CType Ct = Cmp.compare(body(), E->body());
     Cmp.leaveScope();
@@ -859,7 +868,8 @@ public:
     return Vs.reduceCode(*this, Nt, Nb);
   }
 
-  template <class C> typename C::CType compare(Code* E, C& Cmp) {
+  template <class C>
+  typename C::CType compare(const Code* E, C& Cmp) const {
     typename C::CType Ct = Cmp.compare(returnType(), E->returnType());
     if (Cmp.notTrue(Ct))
       return Ct;
@@ -894,7 +904,8 @@ public:
     return Vs.reduceField(*this, Nr, Nb);
   }
 
-  template <class C> typename C::CType compare(Field* E, C& Cmp) {
+  template <class C>
+  typename C::CType compare(const Field* E, C& Cmp) const {
     typename C::CType Ct = Cmp.compare(range(), E->range());
     if (Cmp.notTrue(Ct))
       return Ct;
@@ -930,7 +941,8 @@ public:
     return Vs.reduceApply(*this, Nf, Na);
   }
 
-  template <class C> typename C::CType compare(Apply* E, C& Cmp) {
+  template <class C>
+  typename C::CType compare(const Apply* E, C& Cmp) const {
     typename C::CType Ct = Cmp.compare(fun(), E->fun());
     if (Cmp.notTrue(Ct))
       return Ct;
@@ -958,7 +970,7 @@ public:
   SExpr *arg() { return Arg.get() ? Arg.get() : Sfun.get(); }
   const SExpr *arg() const { return Arg.get() ? Arg.get() : Sfun.get(); }
 
-  bool isDelegation() const { return Arg == nullptr; }
+  bool isDelegation() const { return Arg != nullptr; }
 
   template <class V>
   typename V::R_SExpr traverse(V &Vs, typename V::R_Ctx Ctx) {
@@ -968,7 +980,8 @@ public:
     return Vs.reduceSApply(*this, Nf, Na);
   }
 
-  template <class C> typename C::CType compare(SApply* E, C& Cmp) {
+  template <class C>
+  typename C::CType compare(const SApply* E, C& Cmp) const {
     typename C::CType Ct = Cmp.compare(sfun(), E->sfun());
     if (Cmp.notTrue(Ct) || (!arg() && !E->arg()))
       return Ct;
@@ -989,7 +1002,7 @@ public:
   Project(SExpr *R, StringRef SName)
       : SExpr(COP_Project), Rec(R), SlotName(SName), Cvdecl(nullptr)
   { }
-  Project(SExpr *R, clang::ValueDecl *Cvd)
+  Project(SExpr *R, const clang::ValueDecl *Cvd)
       : SExpr(COP_Project), Rec(R), SlotName(Cvd->getName()), Cvdecl(Cvd)
   { }
   Project(const Project &P, SExpr *R)
@@ -999,7 +1012,13 @@ public:
   SExpr *record() { return Rec.get(); }
   const SExpr *record() const { return Rec.get(); }
 
-  const clang::ValueDecl *clangValueDecl() const { return Cvdecl; }
+  const clang::ValueDecl *clangDecl() const { return Cvdecl; }
+
+  bool isArrow() const { return (Flags & 0x01) != 0; }
+  void setArrow(bool b) {
+    if (b) Flags |= 0x01;
+    else Flags &= 0xFFFE;
+  }
 
   StringRef slotName() const {
     if (Cvdecl)
@@ -1014,7 +1033,8 @@ public:
     return Vs.reduceProject(*this, Nr);
   }
 
-  template <class C> typename C::CType compare(Project* E, C& Cmp) {
+  template <class C>
+  typename C::CType compare(const Project* E, C& Cmp) const {
     typename C::CType Ct = Cmp.compare(record(), E->record());
     if (Cmp.notTrue(Ct))
       return Ct;
@@ -1024,7 +1044,7 @@ public:
 private:
   SExprRef Rec;
   StringRef SlotName;
-  clang::ValueDecl *Cvdecl;
+  const clang::ValueDecl *Cvdecl;
 };
 
 
@@ -1048,7 +1068,8 @@ public:
     return Vs.reduceCall(*this, Nt);
   }
 
-  template <class C> typename C::CType compare(Call* E, C& Cmp) {
+  template <class C>
+  typename C::CType compare(const Call* E, C& Cmp) const {
     return Cmp.compare(target(), E->target());
   }
 
@@ -1082,7 +1103,8 @@ public:
     return Vs.reduceAlloc(*this, Nd);
   }
 
-  template <class C> typename C::CType compare(Alloc* E, C& Cmp) {
+  template <class C>
+  typename C::CType compare(const Alloc* E, C& Cmp) const {
     typename C::CType Ct = Cmp.compareIntegers(kind(), E->kind());
     if (Cmp.notTrue(Ct))
       return Ct;
@@ -1111,7 +1133,8 @@ public:
     return Vs.reduceLoad(*this, Np);
   }
 
-  template <class C> typename C::CType compare(Load* E, C& Cmp) {
+  template <class C>
+  typename C::CType compare(const Load* E, C& Cmp) const {
     return Cmp.compare(pointer(), E->pointer());
   }
 
@@ -1142,7 +1165,8 @@ public:
     return Vs.reduceStore(*this, Np, Nv);
   }
 
-  template <class C> typename C::CType compare(Store* E, C& Cmp) {
+  template <class C>
+  typename C::CType compare(const Store* E, C& Cmp) const {
     typename C::CType Ct = Cmp.compare(destination(), E->destination());
     if (Cmp.notTrue(Ct))
       return Ct;
@@ -1178,7 +1202,8 @@ public:
     return Vs.reduceArrayIndex(*this, Na, Ni);
   }
 
-  template <class C> typename C::CType compare(ArrayIndex* E, C& Cmp) {
+  template <class C>
+  typename C::CType compare(const ArrayIndex* E, C& Cmp) const {
     typename C::CType Ct = Cmp.compare(array(), E->array());
     if (Cmp.notTrue(Ct))
       return Ct;
@@ -1215,7 +1240,8 @@ public:
     return Vs.reduceArrayAdd(*this, Na, Ni);
   }
 
-  template <class C> typename C::CType compare(ArrayAdd* E, C& Cmp) {
+  template <class C>
+  typename C::CType compare(const ArrayAdd* E, C& Cmp) const {
     typename C::CType Ct = Cmp.compare(array(), E->array());
     if (Cmp.notTrue(Ct))
       return Ct;
@@ -1251,7 +1277,8 @@ public:
     return Vs.reduceUnaryOp(*this, Ne);
   }
 
-  template <class C> typename C::CType compare(UnaryOp* E, C& Cmp) {
+  template <class C>
+  typename C::CType compare(const UnaryOp* E, C& Cmp) const {
     typename C::CType Ct =
       Cmp.compareIntegers(unaryOpcode(), E->unaryOpcode());
     if (Cmp.notTrue(Ct))
@@ -1295,7 +1322,8 @@ public:
     return Vs.reduceBinaryOp(*this, Ne0, Ne1);
   }
 
-  template <class C> typename C::CType compare(BinaryOp* E, C& Cmp) {
+  template <class C>
+  typename C::CType compare(const BinaryOp* E, C& Cmp) const {
     typename C::CType Ct =
       Cmp.compareIntegers(binaryOpcode(), E->binaryOpcode());
     if (Cmp.notTrue(Ct))
@@ -1333,7 +1361,8 @@ public:
     return Vs.reduceCast(*this, Ne);
   }
 
-  template <class C> typename C::CType compare(Cast* E, C& Cmp) {
+  template <class C>
+  typename C::CType compare(const Cast* E, C& Cmp) const {
     typename C::CType Ct =
       Cmp.compareIntegers(castOpcode(), E->castOpcode());
     if (Cmp.notTrue(Ct))
@@ -1386,7 +1415,8 @@ public:
     return Vs.reducePhi(*this, Nvs);
   }
 
-  template <class C> typename C::CType compare(Phi *E, C &Cmp) {
+  template <class C>
+  typename C::CType compare(const Phi *E, C &Cmp) const {
     // TODO: implement CFG comparisons
     return Cmp.comparePointers(this, E);
   }
@@ -1503,7 +1533,8 @@ public:
     return Vs.reduceBasicBlock(*this, Nas, Nis, Nt);
   }
 
-  template <class C> typename C::CType compare(BasicBlock *E, C &Cmp) {
+  template <class C>
+  typename C::CType compare(const BasicBlock *E, C &Cmp) const {
     // TODO: implement CFG comparisons
     return Cmp.comparePointers(this, E);
   }
@@ -1590,7 +1621,8 @@ public:
     return Vs.reduceSCFG(*this, Bbs);
   }
 
-  template <class C> typename C::CType compare(SCFG *E, C &Cmp) {
+  template <class C>
+  typename C::CType compare(const SCFG *E, C &Cmp) const {
     // TODO -- implement CFG comparisons
     return Cmp.comparePointers(this, E);
   }
@@ -1623,7 +1655,8 @@ public:
     return Vs.reduceGoto(*this, Ntb);
   }
 
-  template <class C> typename C::CType compare(Goto *E, C &Cmp) {
+  template <class C>
+  typename C::CType compare(const Goto *E, C &Cmp) const {
     // TODO -- implement CFG comparisons
     return Cmp.comparePointers(this, E);
   }
@@ -1668,7 +1701,8 @@ public:
     return Vs.reduceBranch(*this, Nc, Ntb, Nte);
   }
 
-  template <class C> typename C::CType compare(Branch *E, C &Cmp) {
+  template <class C>
+  typename C::CType compare(const Branch *E, C &Cmp) const {
     // TODO -- implement CFG comparisons
     return Cmp.comparePointers(this, E);
   }
@@ -1698,7 +1732,8 @@ public:
     return Vs.reduceIdentifier(*this);
   }
 
-  template <class C> typename C::CType compare(Identifier* E, C& Cmp) {
+  template <class C>
+  typename C::CType compare(const Identifier* E, C& Cmp) const {
     return Cmp.compareStrings(name(), E->name());
   }
 
@@ -1737,7 +1772,8 @@ public:
     return Vs.reduceIfThenElse(*this, Nc, Nt, Ne);
   }
 
-  template <class C> typename C::CType compare(IfThenElse* E, C& Cmp) {
+  template <class C>
+  typename C::CType compare(const IfThenElse* E, C& Cmp) const {
     typename C::CType Ct = Cmp.compare(condition(), E->condition());
     if (Cmp.notTrue(Ct))
       return Ct;
@@ -1784,7 +1820,8 @@ public:
     return Vs.reduceLet(*this, Nvd, E1);
   }
 
-  template <class C> typename C::CType compare(Let* E, C& Cmp) {
+  template <class C>
+  typename C::CType compare(const Let* E, C& Cmp) const {
     typename C::CType Ct =
       Cmp.compare(VarDecl->definition(), E->VarDecl->definition());
     if (Cmp.notTrue(Ct))
@@ -1802,7 +1839,8 @@ private:
 
 
 
-SExpr *getCanonicalVal(SExpr *E);
+const SExpr *getCanonicalVal(const SExpr *E);
+SExpr* simplifyToCanonicalVal(SExpr *E);
 void simplifyIncompleteArg(Variable *V, til::Phi *Ph);
 
 

Modified: cfe/trunk/include/clang/Analysis/Analyses/ThreadSafetyTraverse.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Analysis/Analyses/ThreadSafetyTraverse.h?rev=214089&r1=214088&r2=214089&view=diff
==============================================================================
--- cfe/trunk/include/clang/Analysis/Analyses/ThreadSafetyTraverse.h (original)
+++ cfe/trunk/include/clang/Analysis/Analyses/ThreadSafetyTraverse.h Mon Jul 28 10:57:27 2014
@@ -19,6 +19,8 @@
 
 #include "ThreadSafetyTIL.h"
 
+#include <ostream>
+
 namespace clang {
 namespace threadSafety {
 namespace til {
@@ -423,7 +425,7 @@ protected:
   Self *self() { return reinterpret_cast<Self *>(this); }
 
 public:
-  bool compareByCase(SExpr *E1, SExpr* E2) {
+  bool compareByCase(const SExpr *E1, const SExpr* E2) {
     switch (E1->opcode()) {
 #define TIL_OPCODE_DEF(X)                                                     \
     case COP_##X:                                                             \
@@ -449,38 +451,86 @@ public:
   bool compareStrings (StringRef s, StringRef r)     { return s == r; }
   bool comparePointers(const void* P, const void* Q) { return P == Q; }
 
-  bool compare(SExpr *E1, SExpr* E2) {
+  bool compare(const SExpr *E1, const SExpr* E2) {
     if (E1->opcode() != E2->opcode())
       return false;
     return compareByCase(E1, E2);
   }
 
   // TODO -- handle alpha-renaming of variables
-  void enterScope(Variable* V1, Variable* V2) { }
+  void enterScope(const Variable* V1, const Variable* V2) { }
   void leaveScope() { }
 
-  bool compareVariableRefs(Variable* V1, Variable* V2) {
+  bool compareVariableRefs(const Variable* V1, const Variable* V2) {
     return V1 == V2;
   }
 
-  static bool compareExprs(SExpr *E1, SExpr* E2) {
+  static bool compareExprs(const SExpr *E1, const SExpr* E2) {
     EqualsComparator Eq;
     return Eq.compare(E1, E2);
   }
 };
 
 
+
+class MatchComparator : public Comparator<MatchComparator> {
+public:
+  // Result type for the comparison, e.g. bool for simple equality,
+  // or int for lexigraphic comparison (-1, 0, 1).  Must have one value which
+  // denotes "true".
+  typedef bool CType;
+
+  CType trueResult() { return true; }
+  bool notTrue(CType ct) { return !ct; }
+
+  bool compareIntegers(unsigned i, unsigned j)       { return i == j; }
+  bool compareStrings (StringRef s, StringRef r)     { return s == r; }
+  bool comparePointers(const void* P, const void* Q) { return P == Q; }
+
+  bool compare(const SExpr *E1, const SExpr* E2) {
+    // Wildcards match anything.
+    if (E1->opcode() == COP_Wildcard || E2->opcode() == COP_Wildcard)
+      return true;
+    // otherwise normal equality.
+    if (E1->opcode() != E2->opcode())
+      return false;
+    return compareByCase(E1, E2);
+  }
+
+  // TODO -- handle alpha-renaming of variables
+  void enterScope(const Variable* V1, const Variable* V2) { }
+  void leaveScope() { }
+
+  bool compareVariableRefs(const Variable* V1, const Variable* V2) {
+    return V1 == V2;
+  }
+
+  static bool compareExprs(const SExpr *E1, const SExpr* E2) {
+    MatchComparator Matcher;
+    return Matcher.compare(E1, E2);
+  }
+};
+
+
+
+inline std::ostream& operator<<(std::ostream& SS, llvm::StringRef R) {
+  return SS.write(R.data(), R.size());
+}
+
 // Pretty printer for TIL expressions
 template <typename Self, typename StreamType>
 class PrettyPrinter {
 private:
   bool Verbose;  // Print out additional information
   bool Cleanup;  // Omit redundant decls.
+  bool CStyle;   // Print exprs in C-like syntax.
 
 public:
-  PrettyPrinter(bool V = false, bool C = true) : Verbose(V), Cleanup(C) { }
+  PrettyPrinter(bool V = false, bool C = true, bool CS = true)
+     : Verbose(V), Cleanup(C), CStyle(CS)
+  {}
 
-  static void print(SExpr *E, StreamType &SS) {
+  static void print(const SExpr *E, StreamType &SS) {
     Self printer;
     printer.printSExpr(E, SS, Prec_MAX);
   }
@@ -502,7 +552,7 @@ protected:
   static const unsigned Prec_MAX = 6;
 
   // Return the precedence of a given node, for use in pretty printing.
-  unsigned precedence(SExpr *E) {
+  unsigned precedence(const SExpr *E) {
     switch (E->opcode()) {
       case COP_Future:     return Prec_Atom;
       case COP_Undefined:  return Prec_Atom;
@@ -529,7 +579,7 @@ protected:
 
       case COP_UnaryOp:    return Prec_Unary;
       case COP_BinaryOp:   return Prec_Binary;
-      case COP_Cast:       return Prec_Unary;
+      case COP_Cast:       return Prec_Atom;
 
       case COP_SCFG:       return Prec_Decl;
       case COP_BasicBlock: return Prec_MAX;
@@ -544,7 +594,7 @@ protected:
     return Prec_MAX;
   }
 
-  void printBlockLabel(StreamType & SS, BasicBlock *BB, unsigned index) {
+  void printBlockLabel(StreamType & SS, const BasicBlock *BB, unsigned index) {
     if (!BB) {
       SS << "BB_null";
       return;
@@ -555,7 +605,7 @@ protected:
     SS << index;
   }
 
-  void printSExpr(SExpr *E, StreamType &SS, unsigned P) {
+  void printSExpr(const SExpr *E, StreamType &SS, unsigned P) {
     if (!E) {
       self()->printNull(SS);
       return;
@@ -582,28 +632,28 @@ protected:
     SS << "#null";
   }
 
-  void printFuture(Future *E, StreamType &SS) {
+  void printFuture(const Future *E, StreamType &SS) {
     self()->printSExpr(E->maybeGetResult(), SS, Prec_Atom);
   }
 
-  void printUndefined(Undefined *E, StreamType &SS) {
+  void printUndefined(const Undefined *E, StreamType &SS) {
     SS << "#undefined";
   }
 
-  void printWildcard(Wildcard *E, StreamType &SS) {
-    SS << "_";
+  void printWildcard(const Wildcard *E, StreamType &SS) {
+    SS << "*";
   }
 
   template<class T>
-  void printLiteralT(LiteralT<T> *E, StreamType &SS) {
+  void printLiteralT(const LiteralT<T> *E, StreamType &SS) {
     SS << E->value();
   }
 
-  void printLiteralT(LiteralT<uint8_t> *E, StreamType &SS) {
+  void printLiteralT(const LiteralT<uint8_t> *E, StreamType &SS) {
     SS << "'" << E->value() << "'";
   }
 
-  void printLiteral(Literal *E, StreamType &SS) {
+  void printLiteral(const Literal *E, StreamType &SS) {
     if (E->clangExpr()) {
       SS << getSourceLiteralString(E->clangExpr());
       return;
@@ -685,13 +735,13 @@ protected:
     SS << "#lit";
   }
 
-  void printLiteralPtr(LiteralPtr *E, StreamType &SS) {
+  void printLiteralPtr(const LiteralPtr *E, StreamType &SS) {
     SS << E->clangDecl()->getNameAsString();
   }
 
-  void printVariable(Variable *V, StreamType &SS, bool IsVarDecl = false) {
+  void printVariable(const Variable *V, StreamType &SS, bool IsVarDecl = false) {
     if (!IsVarDecl && Cleanup) {
-      SExpr* E = getCanonicalVal(V);
+      const SExpr* E = getCanonicalVal(V);
       if (E != V) {
         printSExpr(E, SS, Prec_Atom);
         return;
@@ -699,11 +749,13 @@ protected:
     }
     if (V->kind() == Variable::VK_LetBB)
       SS << V->name() << V->getBlockID() << "_" << V->getID();
+    else if (CStyle && V->kind() == Variable::VK_SFun)
+      SS << "this";
     else
       SS << V->name() << V->getID();
   }
 
-  void printFunction(Function *E, StreamType &SS, unsigned sugared = 0) {
+  void printFunction(const Function *E, StreamType &SS, unsigned sugared = 0) {
     switch (sugared) {
       default:
         SS << "\\(";   // Lambda
@@ -719,7 +771,7 @@ protected:
     SS << ": ";
     self()->printSExpr(E->variableDecl()->definition(), SS, Prec_MAX);
 
-    SExpr *B = E->body();
+    const SExpr *B = E->body();
     if (B && B->opcode() == COP_Function)
       self()->printFunction(cast<Function>(B), SS, 2);
     else {
@@ -728,29 +780,29 @@ protected:
     }
   }
 
-  void printSFunction(SFunction *E, StreamType &SS) {
+  void printSFunction(const SFunction *E, StreamType &SS) {
     SS << "@";
     self()->printVariable(E->variableDecl(), SS, true);
     SS << " ";
     self()->printSExpr(E->body(), SS, Prec_Decl);
   }
 
-  void printCode(Code *E, StreamType &SS) {
+  void printCode(const Code *E, StreamType &SS) {
     SS << ": ";
     self()->printSExpr(E->returnType(), SS, Prec_Decl-1);
     SS << " -> ";
     self()->printSExpr(E->body(), SS, Prec_Decl);
   }
 
-  void printField(Field *E, StreamType &SS) {
+  void printField(const Field *E, StreamType &SS) {
     SS << ": ";
     self()->printSExpr(E->range(), SS, Prec_Decl-1);
     SS << " = ";
     self()->printSExpr(E->body(), SS, Prec_Decl);
   }
 
-  void printApply(Apply *E, StreamType &SS, bool sugared = false) {
-    SExpr *F = E->fun();
+  void printApply(const Apply *E, StreamType &SS, bool sugared = false) {
+    const SExpr *F = E->fun();
     if (F->opcode() == COP_Apply) {
       printApply(cast<Apply>(F), SS, true);
       SS << ", ";
@@ -763,7 +815,7 @@ protected:
       SS << ")$";
   }
 
-  void printSApply(SApply *E, StreamType &SS) {
+  void printSApply(const SApply *E, StreamType &SS) {
     self()->printSExpr(E->sfun(), SS, Prec_Postfix);
     if (E->isDelegation()) {
       SS << "@(";
@@ -772,14 +824,36 @@ protected:
     }
   }
 
-  void printProject(Project *E, StreamType &SS) {
+  void printProject(const Project *E, StreamType &SS) {
+    if (CStyle) {
+      // Omit the  this->
+      if (const SApply *SAP = dyn_cast<SApply>(E->record())) {
+        if (const Variable *V = dyn_cast<Variable>(SAP->sfun())) {
+          if (!SAP->isDelegation() && V->kind() == Variable::VK_SFun) {
+            SS << E->slotName();
+            return;
+          }
+        }
+      }
+      if (isa<Wildcard>(E->record())) {
+        // handle existentials
+        SS << "&";
+        SS << E->clangDecl()->getQualifiedNameAsString();
+        return;
+      }
+    }
     self()->printSExpr(E->record(), SS, Prec_Postfix);
-    SS << ".";
+    if (CStyle && E->isArrow()) {
+      SS << "->";
+    }
+    else {
+      SS << ".";
+    }
     SS << E->slotName();
   }
 
-  void printCall(Call *E, StreamType &SS) {
-    SExpr *T = E->target();
+  void printCall(const Call *E, StreamType &SS) {
+    const SExpr *T = E->target();
     if (T->opcode() == COP_Apply) {
       self()->printApply(cast<Apply>(T), SS, true);
       SS << ")";
@@ -790,52 +864,60 @@ protected:
     }
   }
 
-  void printAlloc(Alloc *E, StreamType &SS) {
+  void printAlloc(const Alloc *E, StreamType &SS) {
     SS << "new ";
     self()->printSExpr(E->dataType(), SS, Prec_Other-1);
   }
 
-  void printLoad(Load *E, StreamType &SS) {
+  void printLoad(const Load *E, StreamType &SS) {
     self()->printSExpr(E->pointer(), SS, Prec_Postfix);
-    SS << "^";
+    if (!CStyle)
+      SS << "^";
   }
 
-  void printStore(Store *E, StreamType &SS) {
+  void printStore(const Store *E, StreamType &SS) {
     self()->printSExpr(E->destination(), SS, Prec_Other-1);
     SS << " := ";
     self()->printSExpr(E->source(), SS, Prec_Other-1);
   }
 
-  void printArrayIndex(ArrayIndex *E, StreamType &SS) {
+  void printArrayIndex(const ArrayIndex *E, StreamType &SS) {
     self()->printSExpr(E->array(), SS, Prec_Postfix);
     SS << "[";
     self()->printSExpr(E->index(), SS, Prec_MAX);
     SS << "]";
   }
 
-  void printArrayAdd(ArrayAdd *E, StreamType &SS) {
+  void printArrayAdd(const ArrayAdd *E, StreamType &SS) {
     self()->printSExpr(E->array(), SS, Prec_Postfix);
     SS << " + ";
     self()->printSExpr(E->index(), SS, Prec_Atom);
   }
 
-  void printUnaryOp(UnaryOp *E, StreamType &SS) {
+  void printUnaryOp(const UnaryOp *E, StreamType &SS) {
     SS << getUnaryOpcodeString(E->unaryOpcode());
     self()->printSExpr(E->expr(), SS, Prec_Unary);
   }
 
-  void printBinaryOp(BinaryOp *E, StreamType &SS) {
+  void printBinaryOp(const BinaryOp *E, StreamType &SS) {
     self()->printSExpr(E->expr0(), SS, Prec_Binary-1);
     SS << " " << getBinaryOpcodeString(E->binaryOpcode()) << " ";
     self()->printSExpr(E->expr1(), SS, Prec_Binary-1);
   }
 
-  void printCast(Cast *E, StreamType &SS) {
-    SS << "%";
+  void printCast(const Cast *E, StreamType &SS) {
+    if (!CStyle) {
+      SS << "cast[";
+      SS << E->castOpcode();
+      SS << "](";
+      self()->printSExpr(E->expr(), SS, Prec_Unary);
+      SS << ")";
+      return;
+    }
     self()->printSExpr(E->expr(), SS, Prec_Unary);
   }
 
-  void printSCFG(SCFG *E, StreamType &SS) {
+  void printSCFG(const SCFG *E, StreamType &SS) {
     SS << "CFG {\n";
     for (auto BBI : *E) {
       printBasicBlock(BBI, SS);
@@ -844,7 +926,7 @@ protected:
     newline(SS);
   }
 
-  void printBasicBlock(BasicBlock *E, StreamType &SS) {
+  void printBasicBlock(const BasicBlock *E, StreamType &SS) {
     SS << "BB_" << E->blockID() << ":";
     if (E->parent())
       SS << " BB_" << E->parent()->blockID();
@@ -867,7 +949,7 @@ protected:
       SS << ";";
       newline(SS);
     }
-    SExpr *T = E->terminator();
+    const SExpr *T = E->terminator();
     if (T) {
       self()->printSExpr(T, SS, Prec_MAX);
       SS << ";";
@@ -876,7 +958,7 @@ protected:
     newline(SS);
   }
 
-  void printPhi(Phi *E, StreamType &SS) {
+  void printPhi(const Phi *E, StreamType &SS) {
     SS << "phi(";
     if (E->status() == Phi::PH_SingleVal)
       self()->printSExpr(E->values()[0], SS, Prec_MAX);
@@ -891,12 +973,12 @@ protected:
     SS << ")";
   }
 
-  void printGoto(Goto *E, StreamType &SS) {
+  void printGoto(const Goto *E, StreamType &SS) {
     SS << "goto ";
     printBlockLabel(SS, E->targetBlock(), E->index());
   }
 
-  void printBranch(Branch *E, StreamType &SS) {
+  void printBranch(const Branch *E, StreamType &SS) {
     SS << "branch (";
     self()->printSExpr(E->condition(), SS, Prec_MAX);
     SS << ") ";
@@ -905,11 +987,19 @@ protected:
     printBlockLabel(SS, E->elseBlock(), E->elseIndex());
   }
 
-  void printIdentifier(Identifier *E, StreamType &SS) {
+  void printIdentifier(const Identifier *E, StreamType &SS) {
     SS << E->name();
   }
 
-  void printIfThenElse(IfThenElse *E, StreamType &SS) {
+  void printIfThenElse(const IfThenElse *E, StreamType &SS) {
+    if (CStyle) {
+      printSExpr(E->condition(), SS, Prec_Unary);
+      SS << " ? ";
+      printSExpr(E->thenExpr(), SS, Prec_Unary);
+      SS << " : ";
+      printSExpr(E->elseExpr(), SS, Prec_Unary);
+      return;
+    }
     SS << "if (";
     printSExpr(E->condition(), SS, Prec_MAX);
     SS << ") then ";
@@ -918,7 +1008,7 @@ protected:
     printSExpr(E->elseExpr(), SS, Prec_Other);
   }
 
-  void printLet(Let *E, StreamType &SS) {
+  void printLet(const Let *E, StreamType &SS) {
     SS << "let ";
     printVariable(E->variableDecl(), SS, true);
     SS << " = ";
@@ -929,6 +1019,10 @@ protected:
 };
 
 
+class StdPrinter : public PrettyPrinter<StdPrinter, std::ostream> { };
+
+
+
 } // end namespace til
 } // end namespace threadSafety
 } // end namespace clang

Modified: cfe/trunk/include/clang/Analysis/Analyses/ThreadSafetyUtil.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Analysis/Analyses/ThreadSafetyUtil.h?rev=214089&r1=214088&r2=214089&view=diff
==============================================================================
--- cfe/trunk/include/clang/Analysis/Analyses/ThreadSafetyUtil.h (original)
+++ cfe/trunk/include/clang/Analysis/Analyses/ThreadSafetyUtil.h Mon Jul 28 10:57:27 2014
@@ -144,7 +144,9 @@ public:
   }
 
   iterator begin() { return Data; }
+  const_iterator begin() const { return Data; }
   iterator end() { return Data + Size; }
+  const_iterator end() const { return Data + Size; }
 
   const_iterator cbegin() const { return Data; }
   const_iterator cend() const { return Data + Size; }

Modified: cfe/trunk/lib/Analysis/ThreadSafety.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/ThreadSafety.cpp?rev=214089&r1=214088&r2=214089&view=diff
==============================================================================
--- cfe/trunk/lib/Analysis/ThreadSafety.cpp (original)
+++ cfe/trunk/lib/Analysis/ThreadSafety.cpp Mon Jul 28 10:57:27 2014
@@ -40,682 +40,107 @@
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/raw_ostream.h"
 #include <algorithm>
+#include <ostream>
+#include <sstream>
 #include <utility>
 #include <vector>
 
-using namespace clang;
-using namespace thread_safety;
+
+namespace clang {
+namespace threadSafety {
 
 // Key method definition
 ThreadSafetyHandler::~ThreadSafetyHandler() {}
 
-namespace {
-
-/// SExpr implements a simple expression language that is used to store,
-/// compare, and pretty-print C++ expressions.  Unlike a clang Expr, a SExpr
-/// does not capture surface syntax, and it does not distinguish between
-/// C++ concepts, like pointers and references, that have no real semantic
-/// differences.  This simplicity allows SExprs to be meaningfully compared,
-/// e.g.
-///        (x)          =  x
-///        (*this).foo  =  this->foo
-///        *&a          =  a
-///
-/// Thread-safety analysis works by comparing lock expressions.  Within the
-/// body of a function, an expression such as "x->foo->bar.mu" will resolve to
-/// a particular mutex object at run-time.  Subsequent occurrences of the same
-/// expression (where "same" means syntactic equality) will refer to the same
-/// run-time object if three conditions hold:
-/// (1) Local variables in the expression, such as "x" have not changed.
-/// (2) Values on the heap that affect the expression have not changed.
-/// (3) The expression involves only pure function calls.
-///
-/// The current implementation assumes, but does not verify, that multiple uses
-/// of the same lock expression satisfies these criteria.
-class SExpr {
-private:
-  enum ExprOp {
-    EOP_Nop,       ///< No-op
-    EOP_Wildcard,  ///< Matches anything.
-    EOP_Universal, ///< Universal lock.
-    EOP_This,      ///< This keyword.
-    EOP_NVar,      ///< Named variable.
-    EOP_LVar,      ///< Local variable.
-    EOP_Dot,       ///< Field access
-    EOP_Call,      ///< Function call
-    EOP_MCall,     ///< Method call
-    EOP_Index,     ///< Array index
-    EOP_Unary,     ///< Unary operation
-    EOP_Binary,    ///< Binary operation
-    EOP_Unknown    ///< Catchall for everything else
-  };
-
-
-  class SExprNode {
-   private:
-    unsigned char  Op;     ///< Opcode of the root node
-    unsigned char  Flags;  ///< Additional opcode-specific data
-    unsigned short Sz;     ///< Number of child nodes
-    const void*    Data;   ///< Additional opcode-specific data
-
-   public:
-    SExprNode(ExprOp O, unsigned F, const void* D)
-      : Op(static_cast<unsigned char>(O)),
-        Flags(static_cast<unsigned char>(F)), Sz(1), Data(D)
-    { }
-
-    unsigned size() const        { return Sz; }
-    void     setSize(unsigned S) { Sz = S;    }
-
-    ExprOp   kind() const { return static_cast<ExprOp>(Op); }
-
-    const NamedDecl* getNamedDecl() const {
-      assert(Op == EOP_NVar || Op == EOP_LVar || Op == EOP_Dot);
-      return reinterpret_cast<const NamedDecl*>(Data);
-    }
-
-    const NamedDecl* getFunctionDecl() const {
-      assert(Op == EOP_Call || Op == EOP_MCall);
-      return reinterpret_cast<const NamedDecl*>(Data);
-    }
-
-    bool isArrow() const { return Op == EOP_Dot && Flags == 1; }
-    void setArrow(bool A) { Flags = A ? 1 : 0; }
-
-    unsigned arity() const {
-      switch (Op) {
-        case EOP_Nop:       return 0;
-        case EOP_Wildcard:  return 0;
-        case EOP_Universal: return 0;
-        case EOP_NVar:      return 0;
-        case EOP_LVar:      return 0;
-        case EOP_This:      return 0;
-        case EOP_Dot:       return 1;
-        case EOP_Call:      return Flags+1;  // First arg is function.
-        case EOP_MCall:     return Flags+1;  // First arg is implicit obj.
-        case EOP_Index:     return 2;
-        case EOP_Unary:     return 1;
-        case EOP_Binary:    return 2;
-        case EOP_Unknown:   return Flags;
-      }
-      return 0;
-    }
-
-    bool operator==(const SExprNode& Other) const {
-      // Ignore flags and size -- they don't matter.
-      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);
-    }
-  };
-
-
-  /// \brief Encapsulates the lexical context of a function call.  The lexical
-  /// context includes the arguments to the call, including the implicit object
-  /// argument.  When an attribute containing a mutex expression is attached to
-  /// a method, the expression may refer to formal parameters of the method.
-  /// Actual arguments must be substituted for formal parameters to derive
-  /// the appropriate mutex expression in the lexical context where the function
-  /// is called.  PrevCtx holds the context in which the arguments themselves
-  /// should be evaluated; multiple calling contexts can be chained together
-  /// by the lock_returned attribute.
-  struct CallingContext {
-    const NamedDecl*   AttrDecl;   // The decl to which the attribute is attached.
-    const Expr*        SelfArg;    // Implicit object argument -- e.g. 'this'
-    bool               SelfArrow;  // is Self referred to with -> or .?
-    unsigned           NumArgs;    // Number of funArgs
-    const Expr* const* FunArgs;    // Function arguments
-    CallingContext*    PrevCtx;    // The previous context; or 0 if none.
-
-    CallingContext(const NamedDecl *D)
-        : AttrDecl(D), SelfArg(nullptr), SelfArrow(false), NumArgs(0),
-          FunArgs(nullptr), PrevCtx(nullptr) {}
-  };
-
-  typedef SmallVector<SExprNode, 4> NodeVector;
-
-private:
-  // A SExpr is a list of SExprNodes in prefix order.  The Size field allows
-  // the list to be traversed as a tree.
-  NodeVector NodeVec;
-
-private:
-  unsigned make(ExprOp O, unsigned F = 0, const void *D = nullptr) {
-    NodeVec.push_back(SExprNode(O, F, D));
-    return NodeVec.size() - 1;
-  }
-
-  unsigned makeNop() {
-    return make(EOP_Nop);
-  }
-
-  unsigned makeWildcard() {
-    return make(EOP_Wildcard);
-  }
-
-  unsigned makeUniversal() {
-    return make(EOP_Universal);
-  }
+class TILPrinter :
+  public til::PrettyPrinter<TILPrinter, llvm::raw_ostream> {};
 
-  unsigned makeNamedVar(const NamedDecl *D) {
-    return make(EOP_NVar, 0, D);
-  }
-
-  unsigned makeLocalVar(const NamedDecl *D) {
-    return make(EOP_LVar, 0, D);
-  }
-
-  unsigned makeThis() {
-    return make(EOP_This);
-  }
 
-  unsigned makeDot(const NamedDecl *D, bool Arrow) {
-    return make(EOP_Dot, Arrow ? 1 : 0, D);
-  }
-
-  unsigned makeCall(unsigned NumArgs, const NamedDecl *D) {
-    return make(EOP_Call, NumArgs, D);
-  }
-
-  // Grab the very first declaration of virtual method D
-  const CXXMethodDecl* getFirstVirtualDecl(const CXXMethodDecl *D) {
-    while (true) {
-      D = D->getCanonicalDecl();
-      CXXMethodDecl::method_iterator I = D->begin_overridden_methods(),
-                                     E = D->end_overridden_methods();
-      if (I == E)
-        return D;  // Method does not override anything
-      D = *I;      // FIXME: this does not work with multiple inheritance.
-    }
-    return nullptr;
-  }
-
-  unsigned makeMCall(unsigned NumArgs, const CXXMethodDecl *D) {
-    return make(EOP_MCall, NumArgs, getFirstVirtualDecl(D));
-  }
+/// Issue a warning about an invalid lock expression
+static void warnInvalidLock(ThreadSafetyHandler &Handler,
+                            const Expr *MutexExp, const NamedDecl *D,
+                            const Expr *DeclExp, StringRef Kind) {
+  SourceLocation Loc;
+  if (DeclExp)
+    Loc = DeclExp->getExprLoc();
 
-  unsigned makeIndex() {
-    return make(EOP_Index);
-  }
-
-  unsigned makeUnary() {
-    return make(EOP_Unary);
-  }
-
-  unsigned makeBinary() {
-    return make(EOP_Binary);
-  }
-
-  unsigned makeUnknown(unsigned Arity) {
-    return make(EOP_Unknown, Arity);
-  }
-
-  inline bool isCalleeArrow(const Expr *E) {
-    const MemberExpr *ME = dyn_cast<MemberExpr>(E->IgnoreParenCasts());
-    return ME ? ME->isArrow() : false;
-  }
-
-  /// Build an SExpr from the given C++ expression.
-  /// Recursive function that terminates on DeclRefExpr.
-  /// Note: this function merely creates a SExpr; it does not check to
-  /// ensure that the original expression is a valid mutex expression.
-  ///
-  /// NDeref returns the number of Derefence and AddressOf operations
-  /// preceding the Expr; this is used to decide whether to pretty-print
-  /// SExprs with . or ->.
-  unsigned buildSExpr(const Expr *Exp, CallingContext *CallCtx,
-                      int *NDeref = nullptr) {
-    if (!Exp)
-      return 0;
-
-    if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Exp)) {
-      const NamedDecl *ND = cast<NamedDecl>(DRE->getDecl()->getCanonicalDecl());
-      const ParmVarDecl *PV = dyn_cast_or_null<ParmVarDecl>(ND);
-      if (PV) {
-        const FunctionDecl *FD =
-          cast<FunctionDecl>(PV->getDeclContext())->getCanonicalDecl();
-        unsigned i = PV->getFunctionScopeIndex();
-
-        if (CallCtx && CallCtx->FunArgs &&
-            FD == CallCtx->AttrDecl->getCanonicalDecl()) {
-          // Substitute call arguments for references to function parameters
-          assert(i < CallCtx->NumArgs);
-          return buildSExpr(CallCtx->FunArgs[i], CallCtx->PrevCtx, NDeref);
-        }
-        // Map the param back to the param of the original function declaration.
-        makeNamedVar(FD->getParamDecl(i));
-        return 1;
-      }
-      // Not a function parameter -- just store the reference.
-      makeNamedVar(ND);
-      return 1;
-    } else if (isa<CXXThisExpr>(Exp)) {
-      // Substitute parent for 'this'
-      if (CallCtx && CallCtx->SelfArg) {
-        if (!CallCtx->SelfArrow && NDeref)
-          // 'this' is a pointer, but self is not, so need to take address.
-          --(*NDeref);
-        return buildSExpr(CallCtx->SelfArg, CallCtx->PrevCtx, NDeref);
-      }
-      else {
-        makeThis();
-        return 1;
-      }
-    } else if (const MemberExpr *ME = dyn_cast<MemberExpr>(Exp)) {
-      const NamedDecl *ND = ME->getMemberDecl();
-      int ImplicitDeref = ME->isArrow() ? 1 : 0;
-      unsigned Root = makeDot(ND, false);
-      unsigned Sz = buildSExpr(ME->getBase(), CallCtx, &ImplicitDeref);
-      NodeVec[Root].setArrow(ImplicitDeref > 0);
-      NodeVec[Root].setSize(Sz + 1);
-      return Sz + 1;
-    } else if (const CXXMemberCallExpr *CMCE = dyn_cast<CXXMemberCallExpr>(Exp)) {
-      // When calling a function with a lock_returned attribute, replace
-      // the function call with the expression in lock_returned.
-      const CXXMethodDecl *MD = CMCE->getMethodDecl()->getMostRecentDecl();
-      if (LockReturnedAttr* At = MD->getAttr<LockReturnedAttr>()) {
-        CallingContext LRCallCtx(CMCE->getMethodDecl());
-        LRCallCtx.SelfArg = CMCE->getImplicitObjectArgument();
-        LRCallCtx.SelfArrow = isCalleeArrow(CMCE->getCallee());
-        LRCallCtx.NumArgs = CMCE->getNumArgs();
-        LRCallCtx.FunArgs = CMCE->getArgs();
-        LRCallCtx.PrevCtx = CallCtx;
-        return buildSExpr(At->getArg(), &LRCallCtx);
-      }
-      // Hack to treat smart pointers and iterators as pointers;
-      // ignore any method named get().
-      if (CMCE->getMethodDecl()->getNameAsString() == "get" &&
-          CMCE->getNumArgs() == 0) {
-        if (NDeref && isCalleeArrow(CMCE->getCallee()))
-          ++(*NDeref);
-        return buildSExpr(CMCE->getImplicitObjectArgument(), CallCtx, NDeref);
-      }
-      unsigned NumCallArgs = CMCE->getNumArgs();
-      unsigned Root = makeMCall(NumCallArgs, CMCE->getMethodDecl());
-      unsigned Sz = buildSExpr(CMCE->getImplicitObjectArgument(), CallCtx);
-      const Expr* const* CallArgs = CMCE->getArgs();
-      for (unsigned i = 0; i < NumCallArgs; ++i) {
-        Sz += buildSExpr(CallArgs[i], CallCtx);
-      }
-      NodeVec[Root].setSize(Sz + 1);
-      return Sz + 1;
-    } else if (const CallExpr *CE = dyn_cast<CallExpr>(Exp)) {
-      const FunctionDecl *FD = CE->getDirectCallee()->getMostRecentDecl();
-      if (LockReturnedAttr* At = FD->getAttr<LockReturnedAttr>()) {
-        CallingContext LRCallCtx(CE->getDirectCallee());
-        LRCallCtx.NumArgs = CE->getNumArgs();
-        LRCallCtx.FunArgs = CE->getArgs();
-        LRCallCtx.PrevCtx = CallCtx;
-        return buildSExpr(At->getArg(), &LRCallCtx);
-      }
-      // Treat smart pointers and iterators as pointers;
-      // ignore the * and -> operators.
-      if (const CXXOperatorCallExpr *OE = dyn_cast<CXXOperatorCallExpr>(CE)) {
-        OverloadedOperatorKind k = OE->getOperator();
-        if (k == OO_Star) {
-          if (NDeref) ++(*NDeref);
-          return buildSExpr(OE->getArg(0), CallCtx, NDeref);
-        }
-        else if (k == OO_Arrow) {
-          return buildSExpr(OE->getArg(0), CallCtx, NDeref);
-        }
-      }
-      unsigned NumCallArgs = CE->getNumArgs();
-      unsigned Root = makeCall(NumCallArgs, nullptr);
-      unsigned Sz = buildSExpr(CE->getCallee(), CallCtx);
-      const Expr* const* CallArgs = CE->getArgs();
-      for (unsigned i = 0; i < NumCallArgs; ++i) {
-        Sz += buildSExpr(CallArgs[i], CallCtx);
-      }
-      NodeVec[Root].setSize(Sz+1);
-      return Sz+1;
-    } else if (const BinaryOperator *BOE = dyn_cast<BinaryOperator>(Exp)) {
-      unsigned Root = makeBinary();
-      unsigned Sz = buildSExpr(BOE->getLHS(), CallCtx);
-      Sz += buildSExpr(BOE->getRHS(), CallCtx);
-      NodeVec[Root].setSize(Sz);
-      return Sz;
-    } else if (const UnaryOperator *UOE = dyn_cast<UnaryOperator>(Exp)) {
-      // Ignore & and * operators -- they're no-ops.
-      // However, we try to figure out whether the expression is a pointer,
-      // so we can use . and -> appropriately in error messages.
-      if (UOE->getOpcode() == UO_Deref) {
-        if (NDeref) ++(*NDeref);
-        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);
-      }
-      unsigned Root = makeUnary();
-      unsigned Sz = buildSExpr(UOE->getSubExpr(), CallCtx);
-      NodeVec[Root].setSize(Sz);
-      return Sz;
-    } else if (const ArraySubscriptExpr *ASE =
-               dyn_cast<ArraySubscriptExpr>(Exp)) {
-      unsigned Root = makeIndex();
-      unsigned Sz = buildSExpr(ASE->getBase(), CallCtx);
-      Sz += buildSExpr(ASE->getIdx(), CallCtx);
-      NodeVec[Root].setSize(Sz);
-      return Sz;
-    } else if (const AbstractConditionalOperator *CE =
-               dyn_cast<AbstractConditionalOperator>(Exp)) {
-      unsigned Root = makeUnknown(3);
-      unsigned Sz = buildSExpr(CE->getCond(), CallCtx);
-      Sz += buildSExpr(CE->getTrueExpr(), CallCtx);
-      Sz += buildSExpr(CE->getFalseExpr(), CallCtx);
-      NodeVec[Root].setSize(Sz);
-      return Sz;
-    } else if (const ChooseExpr *CE = dyn_cast<ChooseExpr>(Exp)) {
-      unsigned Root = makeUnknown(3);
-      unsigned Sz = buildSExpr(CE->getCond(), CallCtx);
-      Sz += buildSExpr(CE->getLHS(), CallCtx);
-      Sz += buildSExpr(CE->getRHS(), CallCtx);
-      NodeVec[Root].setSize(Sz);
-      return Sz;
-    } else if (const CastExpr *CE = dyn_cast<CastExpr>(Exp)) {
-      return buildSExpr(CE->getSubExpr(), CallCtx, NDeref);
-    } else if (const ParenExpr *PE = dyn_cast<ParenExpr>(Exp)) {
-      return buildSExpr(PE->getSubExpr(), CallCtx, NDeref);
-    } else if (const ExprWithCleanups *EWC = dyn_cast<ExprWithCleanups>(Exp)) {
-      return buildSExpr(EWC->getSubExpr(), CallCtx, NDeref);
-    } else if (const CXXBindTemporaryExpr *E = dyn_cast<CXXBindTemporaryExpr>(Exp)) {
-      return buildSExpr(E->getSubExpr(), CallCtx, NDeref);
-    } else if (isa<CharacterLiteral>(Exp) ||
-               isa<CXXNullPtrLiteralExpr>(Exp) ||
-               isa<GNUNullExpr>(Exp) ||
-               isa<CXXBoolLiteralExpr>(Exp) ||
-               isa<FloatingLiteral>(Exp) ||
-               isa<ImaginaryLiteral>(Exp) ||
-               isa<IntegerLiteral>(Exp) ||
-               isa<StringLiteral>(Exp) ||
-               isa<ObjCStringLiteral>(Exp)) {
-      makeNop();
-      return 1;  // FIXME: Ignore literals for now
-    } else {
-      makeNop();
-      return 1;  // Ignore.  FIXME: mark as invalid expression?
-    }
-  }
-
-  /// \brief Construct a SExpr from an expression.
-  /// \param MutexExp The original mutex expression within an attribute
-  /// \param DeclExp An expression involving the Decl on which the attribute
-  ///        occurs.
-  /// \param D  The declaration to which the lock/unlock attribute is attached.
-  void buildSExprFromExpr(const Expr *MutexExp, const Expr *DeclExp,
-                          const NamedDecl *D, VarDecl *SelfDecl = nullptr) {
-    CallingContext CallCtx(D);
-
-    if (MutexExp) {
-      if (const StringLiteral* SLit = dyn_cast<StringLiteral>(MutexExp)) {
-        if (SLit->getString() == StringRef("*"))
-          // The "*" expr is a universal lock, which essentially turns off
-          // checks until it is removed from the lockset.
-          makeUniversal();
-        else
-          // Ignore other string literals for now.
-          makeNop();
-        return;
-      }
-    }
-
-    // If we are processing a raw attribute expression, with no substitutions.
-    if (!DeclExp) {
-      buildSExpr(MutexExp, nullptr);
-      return;
-    }
-
-    // Examine DeclExp to find SelfArg and FunArgs, which are used to substitute
-    // for formal parameters when we call buildMutexID later.
-    if (const MemberExpr *ME = dyn_cast<MemberExpr>(DeclExp)) {
-      CallCtx.SelfArg   = ME->getBase();
-      CallCtx.SelfArrow = ME->isArrow();
-    } else if (const CXXMemberCallExpr *CE =
-               dyn_cast<CXXMemberCallExpr>(DeclExp)) {
-      CallCtx.SelfArg   = CE->getImplicitObjectArgument();
-      CallCtx.SelfArrow = isCalleeArrow(CE->getCallee());
-      CallCtx.NumArgs   = CE->getNumArgs();
-      CallCtx.FunArgs   = CE->getArgs();
-    } else if (const CallExpr *CE = dyn_cast<CallExpr>(DeclExp)) {
-      CallCtx.NumArgs = CE->getNumArgs();
-      CallCtx.FunArgs = CE->getArgs();
-    } else if (const CXXConstructExpr *CE =
-               dyn_cast<CXXConstructExpr>(DeclExp)) {
-      CallCtx.SelfArg = nullptr;  // Will be set below
-      CallCtx.NumArgs = CE->getNumArgs();
-      CallCtx.FunArgs = CE->getArgs();
-    } else if (D && isa<CXXDestructorDecl>(D)) {
-      // There's no such thing as a "destructor call" in the AST.
-      CallCtx.SelfArg = DeclExp;
-    }
-
-    // Hack to handle constructors, where self cannot be recovered from
-    // the expression.
-    if (SelfDecl && !CallCtx.SelfArg) {
-      DeclRefExpr SelfDRE(SelfDecl, false, SelfDecl->getType(), VK_LValue,
-                          SelfDecl->getLocation());
-      CallCtx.SelfArg = &SelfDRE;
-
-      // If the attribute has no arguments, then assume the argument is "this".
-      if (!MutexExp)
-        buildSExpr(CallCtx.SelfArg, nullptr);
-      else  // For most attributes.
-        buildSExpr(MutexExp, &CallCtx);
-      return;
-    }
+  // FIXME: add a note about the attribute location in MutexExp or D
+  if (Loc.isValid())
+    Handler.handleInvalidLockExp(Kind, Loc);
+}
 
-    // If the attribute has no arguments, then assume the argument is "this".
-    if (!MutexExp)
-      buildSExpr(CallCtx.SelfArg, nullptr);
-    else  // For most attributes.
-      buildSExpr(MutexExp, &CallCtx);
-  }
 
-  /// \brief Get index of next sibling of node i.
-  unsigned getNextSibling(unsigned i) const {
-    return i + NodeVec[i].size();
-  }
+// Various helper functions on til::SExpr
+namespace sx {
 
-public:
-  explicit SExpr(clang::Decl::EmptyShell e) { NodeVec.clear(); }
-
-  /// \param MutexExp The original mutex expression within an attribute
-  /// \param DeclExp An expression involving the Decl on which the attribute
-  ///        occurs.
-  /// \param D  The declaration to which the lock/unlock attribute is attached.
-  /// Caller must check isValid() after construction.
-  SExpr(const Expr *MutexExp, const Expr *DeclExp, const NamedDecl *D,
-        VarDecl *SelfDecl = nullptr) {
-    buildSExprFromExpr(MutexExp, DeclExp, D, SelfDecl);
-  }
+bool isUniversal(const til::SExpr *E) {
+  return isa<til::Wildcard>(E);
+}
 
-  /// Return true if this is a valid decl sequence.
-  /// Caller must call this by hand after construction to handle errors.
-  bool isValid() const {
-    return !NodeVec.empty();
-  }
+bool equals(const til::SExpr *E1, const til::SExpr *E2) {
+  return til::EqualsComparator::compareExprs(E1, E2);
+}
 
-  bool shouldIgnore() const {
-    // Nop is a mutex that we have decided to deliberately ignore.
-    assert(NodeVec.size() > 0 && "Invalid Mutex");
-    return NodeVec[0].kind() == EOP_Nop;
+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 isUniversal() const {
-    assert(NodeVec.size() > 0 && "Invalid Mutex");
-    return NodeVec[0].kind() == EOP_Universal;
-  }
+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);
 
-  /// Issue a warning about an invalid lock expression
-  static void warnInvalidLock(ThreadSafetyHandler &Handler,
-                              const Expr *MutexExp, const Expr *DeclExp,
-                              const NamedDecl *D, StringRef Kind) {
-    SourceLocation Loc;
-    if (DeclExp)
-      Loc = DeclExp->getExprLoc();
+  return til::MatchComparator::compareExprs(E1, E2);
+}
 
-    // FIXME: add a note about the attribute location in MutexExp or D
-    if (Loc.isValid())
-      Handler.handleInvalidLockExp(Kind, Loc);
-  }
+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();
+}
 
-  bool operator==(const SExpr &other) const {
-    return NodeVec == other.NodeVec;
-  }
+std::string toString(const til::SExpr *E) {
+  std::stringstream ss;
+  til::StdPrinter::print(E, ss);
+  return ss.str();
+}
 
-  bool operator!=(const SExpr &other) const {
-    return !(*this == other);
-  }
+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;
+}
 
-  bool matches(const SExpr &Other, unsigned i = 0, unsigned j = 0) const {
-    if (NodeVec[i].matches(Other.NodeVec[j])) {
-      unsigned ni = NodeVec[i].arity();
-      unsigned nj = Other.NodeVec[j].arity();
-      unsigned n = (ni < nj) ? ni : nj;
-      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;
-  }
+}  // end namespace sx
 
-  // A partial match between a.mu and b.mu returns true a and b have the same
-  // type (and thus mu refers to the same mutex declaration), regardless of
-  // whether a and b are different objects or not.
-  bool partiallyMatches(const SExpr &Other) const {
-    if (NodeVec[0].kind() == EOP_Dot)
-      return NodeVec[0].matches(Other.NodeVec[0]);
-    return false;
-  }
 
-  /// \brief Pretty print a lock expression for use in error messages.
-  std::string toString(unsigned i = 0) const {
-    assert(isValid());
-    if (i >= NodeVec.size())
-      return "";
-
-    const SExprNode* N = &NodeVec[i];
-    switch (N->kind()) {
-      case EOP_Nop:
-        return "_";
-      case EOP_Wildcard:
-        return "(?)";
-      case EOP_Universal:
-        return "*";
-      case EOP_This:
-        return "this";
-      case EOP_NVar:
-      case EOP_LVar: {
-        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;
-        else
-          return S + "." + FieldName;
-      }
-      case EOP_Call: {
-        std::string S = toString(i+1) + "(";
-        unsigned NumArgs = N->arity()-1;
-        unsigned ci = getNextSibling(i+1);
-        for (unsigned k=0; k<NumArgs; ++k, ci = getNextSibling(ci)) {
-          S += toString(ci);
-          if (k+1 < NumArgs) S += ",";
-        }
-        S += ")";
-        return S;
-      }
-      case EOP_MCall: {
-        std::string S = "";
-        if (NodeVec[i+1].kind() != EOP_This)
-          S = toString(i+1) + ".";
-        if (const NamedDecl *D = N->getFunctionDecl())
-          S += D->getNameAsString() + "(";
-        else
-          S += "#(";
-        unsigned NumArgs = N->arity()-1;
-        unsigned ci = getNextSibling(i+1);
-        for (unsigned k=0; k<NumArgs; ++k, ci = getNextSibling(ci)) {
-          S += toString(ci);
-          if (k+1 < NumArgs) S += ",";
-        }
-        S += ")";
-        return S;
-      }
-      case EOP_Index: {
-        std::string S1 = toString(i+1);
-        std::string S2 = toString(i+1 + NodeVec[i+1].size());
-        return S1 + "[" + S2 + "]";
-      }
-      case EOP_Unary: {
-        std::string S = toString(i+1);
-        return "#" + S;
-      }
-      case EOP_Binary: {
-        std::string S1 = toString(i+1);
-        std::string S2 = toString(i+1 + NodeVec[i+1].size());
-        return "(" + S1 + "#" + S2 + ")";
-      }
-      case EOP_Unknown: {
-        unsigned NumChildren = N->arity();
-        if (NumChildren == 0)
-          return "(...)";
-        std::string S = "(";
-        unsigned ci = i+1;
-        for (unsigned j = 0; j < NumChildren; ++j, ci = getNextSibling(ci)) {
-          S += toString(ci);
-          if (j+1 < NumChildren) S += "#";
-        }
-        S += ")";
-        return S;
-      }
-    }
-    return "";
-  }
-};
 
 /// \brief A short list of SExprs
-class MutexIDList : public SmallVector<SExpr, 3> {
+class MutexIDList : public SmallVector<const til::SExpr*, 3> {
 public:
   /// \brief Push M onto list, but discard duplicates.
-  void push_back_nodup(const SExpr& M) {
-    if (end() == std::find(begin(), end(), M))
-      push_back(M);
+  void push_back_nodup(const til::SExpr *E) {
+    iterator It = std::find_if(begin(), end(), [=](const til::SExpr *E2) {
+      return sx::equals(E, E2);
+    });
+    if (It == end())
+      push_back(E);
   }
 };
 
@@ -735,15 +160,15 @@ struct LockData {
   LockKind LKind;
   bool     Asserted;           // for asserted locks
   bool     Managed;            // for ScopedLockable objects
-  SExpr    UnderlyingMutex;    // 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(Decl::EmptyShell())
+      UnderlyingMutex(nullptr)
   {}
 
-  LockData(SourceLocation AcquireLoc, LockKind LKind, const SExpr &Mu)
+  LockData(SourceLocation AcquireLoc, LockKind LKind, const til::SExpr *Mu)
     : AcquireLoc(AcquireLoc), LKind(LKind), Asserted(false), Managed(false),
       UnderlyingMutex(Mu)
   {}
@@ -771,10 +196,10 @@ struct LockData {
 /// in the program execution.  Currently, this is information regarding a lock
 /// that is held at that point.
 struct FactEntry {
-  SExpr    MutID;
+  const til::SExpr *MutID;
   LockData LDat;
 
-  FactEntry(const SExpr& M, const LockData& L)
+  FactEntry(const til::SExpr* M, const LockData& L)
     : MutID(M), LDat(L)
   { }
 };
@@ -789,8 +214,8 @@ private:
   std::vector<FactEntry> Facts;
 
 public:
-  FactID newLock(const SExpr& M, const LockData& L) {
-    Facts.push_back(FactEntry(M,L));
+  FactID newLock(const til::SExpr *M, const LockData& L) {
+    Facts.push_back(FactEntry(M, L));
     return static_cast<unsigned short>(Facts.size() - 1);
   }
 
@@ -824,66 +249,67 @@ public:
 
   bool isEmpty() const { return FactIDs.size() == 0; }
 
-  FactID addLock(FactManager& FM, const SExpr& M, const LockData& L) {
+  FactID addLock(FactManager& FM, const til::SExpr *M, const LockData& L) {
     FactID F = FM.newLock(M, L);
     FactIDs.push_back(F);
     return F;
   }
 
-  bool removeLock(FactManager& FM, const SExpr& M) {
+  bool removeLock(FactManager& FM, const til::SExpr *M) {
     unsigned n = FactIDs.size();
     if (n == 0)
       return false;
 
     for (unsigned i = 0; i < n-1; ++i) {
-      if (FM[FactIDs[i]].MutID.matches(M)) {
+      if (sx::matches(FM[FactIDs[i]].MutID, M)) {
         FactIDs[i] = FactIDs[n-1];
         FactIDs.pop_back();
         return true;
       }
     }
-    if (FM[FactIDs[n-1]].MutID.matches(M)) {
+    if (sx::matches(FM[FactIDs[n-1]].MutID, M)) {
       FactIDs.pop_back();
       return true;
     }
     return false;
   }
 
-  iterator findLockIter(FactManager &FM, const SExpr &M) {
+  iterator findLockIter(FactManager &FM, const til::SExpr *M) {
     return std::find_if(begin(), end(), [&](FactID ID) {
-      return FM[ID].MutID.matches(M);
+      return sx::matches(FM[ID].MutID, M);
     });
   }
 
-  LockData *findLock(FactManager &FM, const SExpr &M) const {
+  LockData *findLock(FactManager &FM, const til::SExpr *M) const {
     auto I = std::find_if(begin(), end(), [&](FactID ID) {
-      return FM[ID].MutID.matches(M);
+      return sx::matches(FM[ID].MutID, M);
     });
 
     return I != end() ? &FM[*I].LDat : nullptr;
   }
 
-  LockData *findLockUniv(FactManager &FM, const SExpr &M) const {
+  LockData *findLockUniv(FactManager &FM, const til::SExpr *M) const {
     auto I = std::find_if(begin(), end(), [&](FactID ID) -> bool {
-      const SExpr &Expr = FM[ID].MutID;
-      return Expr.isUniversal() || Expr.matches(M);
+      const til::SExpr *E = FM[ID].MutID;
+      return sx::isUniversal(E) || sx::matches(E, M);
     });
 
     return I != end() ? &FM[*I].LDat : nullptr;
   }
 
-  FactEntry *findPartialMatch(FactManager &FM, const SExpr &M) const {
+  FactEntry *findPartialMatch(FactManager &FM, const til::SExpr *M) const {
     auto I = std::find_if(begin(), end(), [&](FactID ID) {
-      return FM[ID].MutID.partiallyMatches(M);
+      return sx::partiallyMatches(FM[ID].MutID, M);
     });
 
     return I != end() ? &FM[*I] : nullptr;
   }
 };
 
+
 /// A Lockset maps each SExpr (defined above) to information about how it has
 /// been locked.
-typedef llvm::ImmutableMap<SExpr, LockData> Lockset;
+typedef llvm::ImmutableMap<til::SExpr*, LockData> Lockset;
 typedef llvm::ImmutableMap<const NamedDecl*, unsigned> LocalVarContext;
 
 class LocalVariableMap;
@@ -1408,18 +834,24 @@ static void findBlockLocations(CFG *CFGr
 class ThreadSafetyAnalyzer {
   friend class BuildLockset;
 
+  llvm::BumpPtrAllocator Bpa;
+  threadSafety::til::MemRegionRef Arena;
+  threadSafety::SExprBuilder SxBuilder;
+
   ThreadSafetyHandler       &Handler;
   LocalVariableMap          LocalVarMap;
   FactManager               FactMan;
   std::vector<CFGBlockInfo> BlockInfo;
 
 public:
-  ThreadSafetyAnalyzer(ThreadSafetyHandler &H) : Handler(H) {}
+  ThreadSafetyAnalyzer(ThreadSafetyHandler &H)
+     : Arena(&Bpa), SxBuilder(Arena), Handler(H) {}
 
-  void addLock(FactSet &FSet, const SExpr &Mutex, const LockData &LDat,
+  void addLock(FactSet &FSet, const til::SExpr *Mutex, const LockData &LDat,
                StringRef DiagKind);
-  void removeLock(FactSet &FSet, const SExpr &Mutex, SourceLocation UnlockLoc,
-                  bool FullyRemove, LockKind Kind, StringRef DiagKind);
+  void removeLock(FactSet &FSet, const til::SExpr *Mutex,
+                  SourceLocation UnlockLoc, bool FullyRemove, LockKind Kind,
+                  StringRef DiagKind);
 
   template <typename AttrType>
   void getMutexIDs(MutexIDList &Mtxs, AttrType *Attr, Expr *Exp,
@@ -1533,16 +965,16 @@ 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 SExpr &Mutex,
+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 (Mutex.shouldIgnore())
+  if (sx::shouldIgnore(Mutex))
     return;
 
   if (FSet.findLock(FactMan, Mutex)) {
     if (!LDat.Asserted)
-      Handler.handleDoubleLock(DiagKind, Mutex.toString(), LDat.AcquireLoc);
+      Handler.handleDoubleLock(DiagKind, sx::toString(Mutex), LDat.AcquireLoc);
   } else {
     FSet.addLock(FactMan, Mutex, LDat);
   }
@@ -1552,28 +984,28 @@ 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 SExpr &Mutex,
+void ThreadSafetyAnalyzer::removeLock(FactSet &FSet, const til::SExpr *Mutex,
                                       SourceLocation UnlockLoc,
                                       bool FullyRemove, LockKind ReceivedKind,
                                       StringRef DiagKind) {
-  if (Mutex.shouldIgnore())
+  if (sx::shouldIgnore(Mutex))
     return;
 
   const LockData *LDat = FSet.findLock(FactMan, Mutex);
   if (!LDat) {
-    Handler.handleUnmatchedUnlock(DiagKind, Mutex.toString(), UnlockLoc);
+    Handler.handleUnmatchedUnlock(DiagKind, sx::toString(Mutex), 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, Mutex.toString(), LDat->LKind,
-                                      ReceivedKind, UnlockLoc);
+    Handler.handleIncorrectUnlockKind(DiagKind, sx::toString(Mutex),
+                                      LDat->LKind, ReceivedKind, UnlockLoc);
     return;
   }
 
-  if (LDat->UnderlyingMutex.isValid()) {
+  if (LDat->UnderlyingMutex) {
     // This is scoped lockable object, which manages the real mutex.
     if (FullyRemove) {
       // We're destroying the managing object.
@@ -1585,7 +1017,7 @@ void ThreadSafetyAnalyzer::removeLock(Fa
       // managing object.  Warn on dual release.
       if (!FSet.findLock(FactMan, LDat->UnderlyingMutex)) {
         Handler.handleUnmatchedUnlock(
-            DiagKind, LDat->UnderlyingMutex.toString(), UnlockLoc);
+            DiagKind, sx::toString(LDat->UnderlyingMutex), UnlockLoc);
       }
       FSet.removeLock(FactMan, LDat->UnderlyingMutex);
       return;
@@ -1603,20 +1035,25 @@ void ThreadSafetyAnalyzer::getMutexIDs(M
                                        VarDecl *SelfDecl) {
   if (Attr->args_size() == 0) {
     // The mutex held is the "this" object.
-    SExpr Mu(nullptr, Exp, D, SelfDecl);
-    if (!Mu.isValid())
-      SExpr::warnInvalidLock(Handler, nullptr, Exp, D,
-                             ClassifyDiagnostic(Attr));
-    else
+    til::SExpr *Mu = SxBuilder.translateAttrExpr(nullptr, D, Exp, SelfDecl);
+    if (Mu && isa<til::Undefined>(Mu)) {
+       warnInvalidLock(Handler, nullptr, D, Exp, ClassifyDiagnostic(Attr));
+       return;
+    }
+    //else
+    if (Mu)
       Mtxs.push_back_nodup(Mu);
     return;
   }
 
   for (const auto *Arg : Attr->args()) {
-    SExpr Mu(Arg, Exp, D, SelfDecl);
-    if (!Mu.isValid())
-      SExpr::warnInvalidLock(Handler, Arg, Exp, D, ClassifyDiagnostic(Attr));
-    else
+    til::SExpr *Mu = SxBuilder.translateAttrExpr(Arg, D, Exp, SelfDecl);
+    if (Mu && isa<til::Undefined>(Mu)) {
+       warnInvalidLock(Handler, nullptr, D, Exp, ClassifyDiagnostic(Attr));
+       return;
+    }
+    //else
+    if (Mu)
       Mtxs.push_back_nodup(Mu);
   }
 }
@@ -1845,11 +1282,12 @@ void BuildLockset::warnIfMutexNotHeld(co
                                       StringRef DiagKind) {
   LockKind LK = getLockKindFromAccessKind(AK);
 
-  SExpr Mutex(MutexExp, Exp, D);
-  if (!Mutex.isValid()) {
-    SExpr::warnInvalidLock(Analyzer->Handler, MutexExp, Exp, D, DiagKind);
+  til::SExpr *Mutex = Analyzer->SxBuilder.translateAttrExpr(MutexExp, D, Exp);
+  if (!Mutex) {
+    // TODO: invalid locks?
+    // warnInvalidLock(Analyzer->Handler, MutexExp, D, Exp, DiagKind);
     return;
-  } else if (Mutex.shouldIgnore()) {
+  } else if (sx::shouldIgnore(Mutex)) {
     return;
   }
 
@@ -1861,38 +1299,42 @@ void BuildLockset::warnIfMutexNotHeld(co
     if (FEntry) {
       // Warn that there's no precise match.
       LDat = &FEntry->LDat;
-      std::string PartMatchStr = FEntry->MutID.toString();
+      std::string PartMatchStr = sx::toString(FEntry->MutID);
       StringRef   PartMatchName(PartMatchStr);
-      Analyzer->Handler.handleMutexNotHeld(DiagKind, D, POK, Mutex.toString(),
+      Analyzer->Handler.handleMutexNotHeld(DiagKind, D, POK,
+                                           sx::toString(Mutex),
                                            LK, Exp->getExprLoc(),
                                            &PartMatchName);
     } else {
       // Warn that there's no match at all.
-      Analyzer->Handler.handleMutexNotHeld(DiagKind, D, POK, Mutex.toString(),
+      Analyzer->Handler.handleMutexNotHeld(DiagKind, D, POK,
+                                           sx::toString(Mutex),
                                            LK, Exp->getExprLoc());
     }
     NoError = false;
   }
   // Make sure the mutex we found is the right kind.
   if (NoError && LDat && !LDat->isAtLeast(LK))
-    Analyzer->Handler.handleMutexNotHeld(DiagKind, D, POK, Mutex.toString(), LK,
-                                         Exp->getExprLoc());
+    Analyzer->Handler.handleMutexNotHeld(DiagKind, D, POK,
+                                         sx::toString(Mutex),
+                                         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) {
-  SExpr Mutex(MutexExp, Exp, D);
-  if (!Mutex.isValid()) {
-    SExpr::warnInvalidLock(Analyzer->Handler, MutexExp, Exp, D, DiagKind);
+  til::SExpr *Mutex = Analyzer->SxBuilder.translateAttrExpr(MutexExp, D, Exp);
+  if (!Mutex) {
+    // TODO: invalid locks?
+    // warnInvalidLock(Analyzer->Handler, MutexExp, D, Exp, DiagKind);
     return;
   }
 
   LockData* LDat = FSet.findLock(Analyzer->FactMan, Mutex);
   if (LDat)
     Analyzer->Handler.handleFunExcludesLock(
-        DiagKind, D->getNameAsString(), Mutex.toString(), Exp->getExprLoc());
+        DiagKind, D->getNameAsString(), sx::toString(Mutex), Exp->getExprLoc());
 }
 
 /// \brief Checks guarded_by and pt_guarded_by attributes.
@@ -2085,7 +1527,7 @@ void BuildLockset::handleCall(Expr *Exp,
   if (isScopedVar) {
     SourceLocation MLoc = VD->getLocation();
     DeclRefExpr DRE(VD, false, VD->getType(), VK_LValue, VD->getLocation());
-    SExpr SMutex(&DRE, nullptr, nullptr);
+    til::SExpr *SMutex = Analyzer->SxBuilder.translateAttrExpr(&DRE, nullptr);
 
     for (const auto &M : ExclusiveLocksToAdd)
       Analyzer->addLock(FSet, SMutex, LockData(MLoc, LK_Exclusive, M),
@@ -2093,6 +1535,12 @@ void BuildLockset::handleCall(Expr *Exp,
     for (const auto &M : SharedLocksToAdd)
       Analyzer->addLock(FSet, SMutex, LockData(MLoc, LK_Shared, M),
                         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),
+                        CapDiagKind);
+    }
   }
 
   // Remove locks.
@@ -2254,14 +1702,14 @@ void ThreadSafetyAnalyzer::intersectAndW
 
   // Find locks in FSet2 that conflict or are not in FSet1, and warn.
   for (const auto &Fact : FSet2) {
-    const SExpr &FSet2Mutex = FactMan[Fact].MutID;
+    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", FSet2Mutex.toString(),
+        Handler.handleExclusiveAndShared("mutex", sx::toString(FSet2Mutex),
                                          LDat2.AcquireLoc, LDat1->AcquireLoc);
         if (Modify && LDat1->LKind != LK_Exclusive) {
           // Take the exclusive lock, which is the one in FSet2.
@@ -2273,40 +1721,42 @@ void ThreadSafetyAnalyzer::intersectAndW
         *I1 = Fact;
       }
     } else {
-      if (LDat2.UnderlyingMutex.isValid()) {
+      if (LDat2.UnderlyingMutex) {
         if (FSet2.findLock(FactMan, LDat2.UnderlyingMutex)) {
           // 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",
-                                            LDat2.UnderlyingMutex.toString(),
+                                            sx::toString(LDat2.UnderlyingMutex),
                                             LDat2.AcquireLoc, JoinLoc, LEK1);
         }
       }
-      else if (!LDat2.Managed && !FSet2Mutex.isUniversal() && !LDat2.Asserted)
-        Handler.handleMutexHeldEndOfScope("mutex", FSet2Mutex.toString(),
+      else if (!LDat2.Managed && !sx::isUniversal(FSet2Mutex) &&
+               !LDat2.Asserted)
+        Handler.handleMutexHeldEndOfScope("mutex", sx::toString(FSet2Mutex),
                                           LDat2.AcquireLoc, JoinLoc, LEK1);
     }
   }
 
   // Find locks in FSet1 that are not in FSet2, and remove them.
   for (const auto &Fact : FSet1Orig) {
-    const SExpr &FSet1Mutex = FactMan[Fact].MutID;
+    const til::SExpr *FSet1Mutex = FactMan[Fact].MutID;
     const LockData &LDat1 = FactMan[Fact].LDat;
 
     if (!FSet2.findLock(FactMan, FSet1Mutex)) {
-      if (LDat1.UnderlyingMutex.isValid()) {
+      if (LDat1.UnderlyingMutex) {
         if (FSet1Orig.findLock(FactMan, LDat1.UnderlyingMutex)) {
           // 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",
-                                            LDat1.UnderlyingMutex.toString(),
+                                            sx::toString(LDat1.UnderlyingMutex),
                                             LDat1.AcquireLoc, JoinLoc, LEK1);
         }
       }
-      else if (!LDat1.Managed && !FSet1Mutex.isUniversal() && !LDat1.Asserted)
-        Handler.handleMutexHeldEndOfScope("mutex", FSet1Mutex.toString(),
+      else if (!LDat1.Managed && !sx::isUniversal(FSet1Mutex) &&
+               !LDat1.Asserted)
+        Handler.handleMutexHeldEndOfScope("mutex", sx::toString(FSet1Mutex),
                                           LDat1.AcquireLoc, JoinLoc, LEK2);
       if (Modify)
         FSet1.removeLock(FactMan, FSet1Mutex);
@@ -2618,11 +2068,6 @@ void ThreadSafetyAnalyzer::runAnalysis(A
                    false);
 }
 
-} // end anonymous namespace
-
-
-namespace clang {
-namespace thread_safety {
 
 /// \brief Check a function's CFG for thread-safety violations.
 ///
@@ -2647,4 +2092,4 @@ LockKind getLockKindFromAccessKind(Acces
   llvm_unreachable("Unknown AccessKind");
 }
 
-}} // end namespace clang::thread_safety
+}} // end namespace clang::threadSafety

Modified: cfe/trunk/lib/Analysis/ThreadSafetyCommon.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/ThreadSafetyCommon.cpp?rev=214089&r1=214088&r2=214089&view=diff
==============================================================================
--- cfe/trunk/lib/Analysis/ThreadSafetyCommon.cpp (original)
+++ cfe/trunk/lib/Analysis/ThreadSafetyCommon.cpp Mon Jul 28 10:57:27 2014
@@ -91,6 +91,102 @@ til::SCFG *SExprBuilder::buildCFG(CFGWal
 }
 
 
+
+inline bool isCalleeArrow(const Expr *E) {
+  const MemberExpr *ME = dyn_cast<MemberExpr>(E->IgnoreParenCasts());
+  return ME ? ME->isArrow() : false;
+}
+
+
+/// \brief Translate a clang expression in an attribute to a til::SExpr.
+/// Constructs the context from D, DeclExp, and SelfDecl.
+///
+/// \param AttrExp The expression to translate.
+/// \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) {
+  // If we are processing a raw attribute expression, with no substitutions.
+  if (!DeclExp)
+    return translateAttrExpr(AttrExp, nullptr);
+
+  CallingContext Ctx(nullptr, D);
+
+  // Examine DeclExp to find SelfArg and FunArgs, which are used to substitute
+  // for formal parameters when we call buildMutexID later.
+  if (const MemberExpr *ME = dyn_cast<MemberExpr>(DeclExp)) {
+    Ctx.SelfArg   = ME->getBase();
+    Ctx.SelfArrow = ME->isArrow();
+  } else if (const CXXMemberCallExpr *CE =
+             dyn_cast<CXXMemberCallExpr>(DeclExp)) {
+    Ctx.SelfArg   = CE->getImplicitObjectArgument();
+    Ctx.SelfArrow = isCalleeArrow(CE->getCallee());
+    Ctx.NumArgs   = CE->getNumArgs();
+    Ctx.FunArgs   = CE->getArgs();
+  } else if (const CallExpr *CE = dyn_cast<CallExpr>(DeclExp)) {
+    Ctx.NumArgs = CE->getNumArgs();
+    Ctx.FunArgs = CE->getArgs();
+  } else if (const CXXConstructExpr *CE =
+             dyn_cast<CXXConstructExpr>(DeclExp)) {
+    Ctx.SelfArg = nullptr;  // Will be set below
+    Ctx.NumArgs = CE->getNumArgs();
+    Ctx.FunArgs = CE->getArgs();
+  } else if (D && isa<CXXDestructorDecl>(D)) {
+    // There's no such thing as a "destructor call" in the AST.
+    Ctx.SelfArg = DeclExp;
+  }
+
+  // Hack to handle constructors, where self cannot be recovered from
+  // the expression.
+  if (SelfDecl && !Ctx.SelfArg) {
+    DeclRefExpr SelfDRE(SelfDecl, false, SelfDecl->getType(), VK_LValue,
+                        SelfDecl->getLocation());
+    Ctx.SelfArg = &SelfDRE;
+
+    // If the attribute has no arguments, then assume the argument is "this".
+    if (!AttrExp)
+      return translateAttrExpr(Ctx.SelfArg, nullptr);
+    else  // For most attributes.
+      return translateAttrExpr(AttrExp, &Ctx);
+  }
+
+  // If the attribute has no arguments, then assume the argument is "this".
+  if (!AttrExp)
+    return translateAttrExpr(Ctx.SelfArg, nullptr);
+  else  // For most attributes.
+    return translateAttrExpr(AttrExp, &Ctx);
+}
+
+
+/// \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)) {
+    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();
+    else
+      // Ignore other string literals for now.
+      return nullptr;
+  }
+
+  til::SExpr *E = translate(AttrExp, Ctx);
+
+  // 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 E;
+}
+
+
+
 // Translate a clang statement or expression to a TIL expression.
 // Also performs substitution of variables; Ctx provides the context.
 // Dispatches on the type of S.
@@ -125,9 +221,10 @@ til::SExpr *SExprBuilder::translate(cons
   case Stmt::ArraySubscriptExprClass:
     return translateArraySubscriptExpr(cast<ArraySubscriptExpr>(S), Ctx);
   case Stmt::ConditionalOperatorClass:
-    return translateConditionalOperator(cast<ConditionalOperator>(S), Ctx);
+    return translateAbstractConditionalOperator(
+             cast<ConditionalOperator>(S), Ctx);
   case Stmt::BinaryConditionalOperatorClass:
-    return translateBinaryConditionalOperator(
+    return translateAbstractConditionalOperator(
              cast<BinaryConditionalOperator>(S), Ctx);
 
   // We treat these as no-ops
@@ -162,6 +259,7 @@ til::SExpr *SExprBuilder::translate(cons
 }
 
 
+
 til::SExpr *SExprBuilder::translateDeclRefExpr(const DeclRefExpr *DRE,
                                                CallingContext *Ctx) {
   const ValueDecl *VD = cast<ValueDecl>(DRE->getDecl()->getCanonicalDecl());
@@ -197,17 +295,72 @@ til::SExpr *SExprBuilder::translateCXXTh
 }
 
 
+const ValueDecl *getValueDeclFromSExpr(const til::SExpr *E) {
+  if (auto *V = dyn_cast<til::Variable>(E))
+    return V->clangDecl();
+  if (auto *P = dyn_cast<til::Project>(E))
+    return P->clangDecl();
+  if (auto *L = dyn_cast<til::LiteralPtr>(E))
+    return L->clangDecl();
+  return 0;
+}
+
+bool hasCppPointerType(const til::SExpr *E) {
+  auto *VD = getValueDeclFromSExpr(E);
+  if (VD && VD->getType()->isPointerType())
+    return true;
+  if (auto *C = dyn_cast<til::Cast>(E))
+    return C->castOpcode() == til::CAST_objToPtr;
+
+  return false;
+}
+
+
+// Grab the very first declaration of virtual method D
+const CXXMethodDecl* getFirstVirtualDecl(const CXXMethodDecl *D) {
+  while (true) {
+    D = D->getCanonicalDecl();
+    CXXMethodDecl::method_iterator I = D->begin_overridden_methods(),
+                                   E = D->end_overridden_methods();
+    if (I == E)
+      return D;  // Method does not override anything
+    D = *I;      // FIXME: this does not work with multiple inheritance.
+  }
+  return nullptr;
+}
+
 til::SExpr *SExprBuilder::translateMemberExpr(const MemberExpr *ME,
                                               CallingContext *Ctx) {
-  til::SExpr *E = translate(ME->getBase(), Ctx);
-  E = new (Arena) til::SApply(E);
-  return new (Arena) til::Project(E, ME->getMemberDecl());
+  til::SExpr *BE = translate(ME->getBase(), Ctx);
+  til::SExpr *E  = new (Arena) til::SApply(BE);
+
+  const ValueDecl *D = ME->getMemberDecl();
+  if (auto *VD = dyn_cast<CXXMethodDecl>(D))
+    D = getFirstVirtualDecl(VD);
+
+  til::Project *P = new (Arena) til::Project(E, D);
+  if (hasCppPointerType(BE))
+    P->setArrow(true);
+  return P;
 }
 
 
 til::SExpr *SExprBuilder::translateCallExpr(const CallExpr *CE,
-                                            CallingContext *Ctx) {
-  // TODO -- Lock returned
+                                            CallingContext *Ctx,
+                                            const Expr *SelfE) {
+  if (CapabilityExprMode) {
+    // Handle LOCK_RETURNED
+    const FunctionDecl *FD = CE->getDirectCallee()->getMostRecentDecl();
+    if (LockReturnedAttr* At = FD->getAttr<LockReturnedAttr>()) {
+      CallingContext LRCallCtx(Ctx);
+      LRCallCtx.AttrDecl = CE->getDirectCallee();
+      LRCallCtx.SelfArg  = SelfE;
+      LRCallCtx.NumArgs  = CE->getNumArgs();
+      LRCallCtx.FunArgs  = CE->getArgs();
+      return translateAttrExpr(At->getArg(), &LRCallCtx);
+    }
+  }
+
   til::SExpr *E = translate(CE->getCallee(), Ctx);
   for (const auto *Arg : CE->arguments()) {
     til::SExpr *A = translate(Arg, Ctx);
@@ -219,12 +372,31 @@ til::SExpr *SExprBuilder::translateCallE
 
 til::SExpr *SExprBuilder::translateCXXMemberCallExpr(
     const CXXMemberCallExpr *ME, CallingContext *Ctx) {
-  return translateCallExpr(cast<CallExpr>(ME), Ctx);
+  if (CapabilityExprMode) {
+    // Ignore calls to get() on smart pointers.
+    if (ME->getMethodDecl()->getNameAsString() == "get" &&
+        ME->getNumArgs() == 0) {
+      auto *E = translate(ME->getImplicitObjectArgument(), Ctx);
+      return new (Arena) til::Cast(til::CAST_objToPtr, E);
+      // return E;
+    }
+  }
+  return translateCallExpr(cast<CallExpr>(ME), Ctx,
+                           ME->getImplicitObjectArgument());
 }
 
 
 til::SExpr *SExprBuilder::translateCXXOperatorCallExpr(
     const CXXOperatorCallExpr *OCE, CallingContext *Ctx) {
+  if (CapabilityExprMode) {
+    // Ignore operator * and operator -> on smart pointers.
+    OverloadedOperatorKind k = OCE->getOperator();
+    if (k == OO_Star || k == OO_Arrow) {
+      auto *E = translate(OCE->getArg(0), Ctx);
+      return new (Arena) til::Cast(til::CAST_objToPtr, E);
+      // return E;
+    }
+  }
   return translateCallExpr(cast<CallExpr>(OCE), Ctx);
 }
 
@@ -238,8 +410,23 @@ til::SExpr *SExprBuilder::translateUnary
   case UO_PreDec:
     return new (Arena) til::Undefined(UO);
 
+  case UO_AddrOf: {
+    if (CapabilityExprMode) {
+      // interpret &Graph::mu_ as an existential.
+      if (DeclRefExpr* DRE = dyn_cast<DeclRefExpr>(UO->getSubExpr())) {
+        if (DRE->getDecl()->isCXXInstanceMember()) {
+          // This is a pointer-to-member expression, e.g. &MyClass::mu_.
+          // We interpret this syntax specially, as a wildcard.
+          auto *W = new (Arena) til::Wildcard();
+          return new (Arena) til::Project(W, DRE->getDecl());
+        }
+      }
+    }
+    // otherwise, & is a no-op
+    return translate(UO->getSubExpr(), Ctx);
+  }
+
   // We treat these as no-ops
-  case UO_AddrOf:
   case UO_Deref:
   case UO_Plus:
     return translate(UO->getSubExpr(), Ctx);
@@ -360,7 +547,9 @@ til::SExpr *SExprBuilder::translateCastE
         return E0;
     }
     til::SExpr *E0 = translate(CE->getSubExpr(), Ctx);
-    return new (Arena) til::Load(E0);
+    return E0;
+    // FIXME!! -- get Load working properly
+    // return new (Arena) til::Load(E0);
   }
   case CK_NoOp:
   case CK_DerivedToBase:
@@ -373,6 +562,8 @@ til::SExpr *SExprBuilder::translateCastE
   default: {
     // FIXME: handle different kinds of casts.
     til::SExpr *E0 = translate(CE->getSubExpr(), Ctx);
+    if (CapabilityExprMode)
+      return E0;
     return new (Arena) til::Cast(til::CAST_none, E0);
   }
   }
@@ -389,15 +580,12 @@ SExprBuilder::translateArraySubscriptExp
 
 
 til::SExpr *
-SExprBuilder::translateConditionalOperator(const ConditionalOperator *C,
-                                           CallingContext *Ctx) {
-  return new (Arena) til::Undefined(C);
-}
-
-
-til::SExpr *SExprBuilder::translateBinaryConditionalOperator(
-    const BinaryConditionalOperator *C, CallingContext *Ctx) {
-  return new (Arena) til::Undefined(C);
+SExprBuilder::translateAbstractConditionalOperator(
+    const AbstractConditionalOperator *CO, CallingContext *Ctx) {
+  auto *C = translate(CO->getCond(), Ctx);
+  auto *T = translate(CO->getTrueExpr(), Ctx);
+  auto *E = translate(CO->getFalseExpr(), Ctx);
+  return new (Arena) til::IfThenElse(C, T, E);
 }
 
 
@@ -430,9 +618,7 @@ SExprBuilder::translateDeclStmt(const De
 // If E is trivial returns E.
 til::SExpr *SExprBuilder::addStatement(til::SExpr* E, const Stmt *S,
                                        const ValueDecl *VD) {
-  if (!E)
-    return nullptr;
-  if (til::ThreadSafetyTIL::isTrivial(E))
+  if (!E || !CurrentBB || til::ThreadSafetyTIL::isTrivial(E))
     return E;
 
   til::Variable *V = new (Arena) til::Variable(E, VD);
@@ -631,7 +817,6 @@ void SExprBuilder::enterCFG(CFG *Cfg, co
     BB->reserveInstructions(B->size());
     BlockMap[B->getBlockID()] = BB;
   }
-  CallCtx.reset(new SExprBuilder::CallingContext(D));
 
   CurrentBB = lookupBlock(&Cfg->getEntry());
   auto Parms = isa<ObjCMethodDecl>(D) ? cast<ObjCMethodDecl>(D)->parameters()
@@ -697,7 +882,7 @@ void SExprBuilder::enterCFGBlockBody(con
 
 
 void SExprBuilder::handleStatement(const Stmt *S) {
-  til::SExpr *E = translate(S, CallCtx.get());
+  til::SExpr *E = translate(S, nullptr);
   addStatement(E, S);
 }
 
@@ -730,7 +915,7 @@ void SExprBuilder::exitCFGBlockBody(cons
     CurrentBB->setTerminator(Tm);
   }
   else if (N == 2) {
-    til::SExpr *C = translate(B->getTerminatorCondition(true), CallCtx.get());
+    til::SExpr *C = translate(B->getTerminatorCondition(true), nullptr);
     til::BasicBlock *BB1 = *It ? lookupBlock(*It) : nullptr;
     ++It;
     til::BasicBlock *BB2 = *It ? lookupBlock(*It) : nullptr;
@@ -775,18 +960,15 @@ void SExprBuilder::exitCFG(const CFGBloc
 }
 
 
-
-class TILPrinter : public til::PrettyPrinter<TILPrinter, llvm::raw_ostream> {};
-
-
+/*
 void printSCFG(CFGWalker &Walker) {
   llvm::BumpPtrAllocator Bpa;
   til::MemRegionRef Arena(&Bpa);
-  SExprBuilder builder(Arena);
-  til::SCFG *Cfg = builder.buildCFG(Walker);
-  TILPrinter::print(Cfg, llvm::errs());
+  SExprBuilder SxBuilder(Arena);
+  til::SCFG *Scfg = SxBuilder.buildCFG(Walker);
+  TILPrinter::print(Scfg, llvm::errs());
 }
-
+*/
 
 
 } // end namespace threadSafety

Modified: cfe/trunk/lib/Analysis/ThreadSafetyTIL.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/ThreadSafetyTIL.cpp?rev=214089&r1=214088&r2=214089&view=diff
==============================================================================
--- cfe/trunk/lib/Analysis/ThreadSafetyTIL.cpp (original)
+++ cfe/trunk/lib/Analysis/ThreadSafetyTIL.cpp Mon Jul 28 10:57:27 2014
@@ -88,10 +88,42 @@ void SCFG::renumberVars() {
 
 
 
+// If E is a variable, then trace back through any aliases or redundant
+// Phi nodes to find the canonical definition.
+const SExpr *getCanonicalVal(const SExpr *E) {
+  while (auto *V = dyn_cast<Variable>(E)) {
+    const SExpr *D;
+    do {
+      if (V->kind() != Variable::VK_Let)
+        return V;
+      D = V->definition();
+      auto *V2 = dyn_cast<Variable>(D);
+      if (V2)
+        V = V2;
+      else
+        break;
+    } while (true);
+
+    if (ThreadSafetyTIL::isTrivial(D))
+      return D;
+
+    if (const Phi *Ph = dyn_cast<Phi>(D)) {
+      if (Ph->status() == Phi::PH_SingleVal) {
+        E = Ph->values()[0];
+        continue;
+      }
+    }
+    return V;
+  }
+  return E;
+}
+
+
 
 // If E is a variable, then trace back through any aliases or redundant
 // Phi nodes to find the canonical definition.
-SExpr *getCanonicalVal(SExpr *E) {
+// The non-const version will simplify incomplete Phi nodes.
+SExpr *simplifyToCanonicalVal(SExpr *E) {
   while (auto *V = dyn_cast<Variable>(E)) {
     SExpr *D;
     do {
@@ -123,6 +155,7 @@ SExpr *getCanonicalVal(SExpr *E) {
 }
 
 
+
 // Trace the arguments of an incomplete Phi node to see if they have the same
 // canonical definition.  If so, mark the Phi node as redundant.
 // getCanonicalVal() will recursively call simplifyIncompletePhi().
@@ -132,9 +165,9 @@ void simplifyIncompleteArg(Variable *V,
   // eliminate infinite recursion -- assume that this node is not redundant.
   Ph->setStatus(Phi::PH_MultiVal);
 
-  SExpr *E0 = getCanonicalVal(Ph->values()[0]);
+  SExpr *E0 = simplifyToCanonicalVal(Ph->values()[0]);
   for (unsigned i=1, n=Ph->values().size(); i<n; ++i) {
-    SExpr *Ei = getCanonicalVal(Ph->values()[i]);
+    SExpr *Ei = simplifyToCanonicalVal(Ph->values()[i]);
     if (Ei == V)
       continue;  // Recursive reference to itself.  Don't count.
     if (Ei != E0) {

Modified: cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp?rev=214089&r1=214088&r2=214089&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp (original)
+++ cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp Mon Jul 28 10:57:27 2014
@@ -1444,9 +1444,9 @@ struct SortDiagBySourceLocation {
 // -Wthread-safety
 //===----------------------------------------------------------------------===//
 namespace clang {
-namespace thread_safety {
-namespace {
-class ThreadSafetyReporter : public clang::thread_safety::ThreadSafetyHandler {
+namespace threadSafety {
+
+class ThreadSafetyReporter : public clang::threadSafety::ThreadSafetyHandler {
   Sema &S;
   DiagList Warnings;
   SourceLocation FunLocation, FunEndLocation;
@@ -1608,7 +1608,7 @@ class ThreadSafetyReporter : public clan
     Warnings.push_back(DelayedDiag(Warning, OptionalNotes()));
   }
 };
-}
+
 }
 }
 
@@ -1896,11 +1896,11 @@ AnalysisBasedWarnings::IssueWarnings(sem
   if (P.enableThreadSafetyAnalysis) {
     SourceLocation FL = AC.getDecl()->getLocation();
     SourceLocation FEL = AC.getDecl()->getLocEnd();
-    thread_safety::ThreadSafetyReporter Reporter(S, FL, FEL);
+    threadSafety::ThreadSafetyReporter Reporter(S, FL, FEL);
     if (!Diags.isIgnored(diag::warn_thread_safety_beta, D->getLocStart()))
       Reporter.setIssueBetaWarnings(true);
 
-    thread_safety::runThreadSafetyAnalysis(AC, Reporter);
+    threadSafety::runThreadSafetyAnalysis(AC, Reporter);
     Reporter.emitDiagnostics();
   }
 

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=214089&r1=214088&r2=214089&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/warn-thread-safety-analysis.cpp (original)
+++ cfe/trunk/test/SemaCXX/warn-thread-safety-analysis.cpp Mon Jul 28 10:57:27 2014
@@ -95,6 +95,13 @@ public:
 };
 
 
+template <class K, class T>
+class MyMap {
+public:
+  T& operator[](const K& k);
+};
+
+
 
 Mutex sls_mu;
 
@@ -2280,6 +2287,15 @@ void test() {
   (a > 0 ? fooArray[1] : fooArray[b]).mu_.Lock();
   (a > 0 ? fooArray[1] : fooArray[b]).a = 0;
   (a > 0 ? fooArray[1] : fooArray[b]).mu_.Unlock();
+}
+
+
+void test2() {
+  Foo *fooArray;
+  Bar bar;
+  int a;
+  int b;
+  int c;
 
   bar.getFoo().mu_.Lock();
   bar.getFooey().a = 0; // \
@@ -2295,20 +2311,20 @@ void test() {
 
   bar.getFoo3(a, b).mu_.Lock();
   bar.getFoo3(a, c).a = 0;  // \
-    // expected-warning {{writing variable 'a' requires holding mutex 'bar.getFoo3(a,c).mu_' exclusively}} \
-    // expected-note {{'bar.getFoo3(a,b).mu_'}}
+    // expected-warning {{writing variable 'a' requires holding mutex 'bar.getFoo3(a, c).mu_' exclusively}} \
+    // expected-note {{found near match 'bar.getFoo3(a, b).mu_'}}
   bar.getFoo3(a, b).mu_.Unlock();
 
   getBarFoo(bar, a).mu_.Lock();
   getBarFoo(bar, b).a = 0;  // \
-    // expected-warning {{writing variable 'a' requires holding mutex 'getBarFoo(bar,b).mu_' exclusively}} \
-    // expected-note {{'getBarFoo(bar,a).mu_'}}
+    // expected-warning {{writing variable 'a' requires holding mutex 'getBarFoo(bar, b).mu_' exclusively}} \
+    // expected-note {{found near match 'getBarFoo(bar, a).mu_'}}
   getBarFoo(bar, a).mu_.Unlock();
 
   (a > 0 ? fooArray[1] : fooArray[b]).mu_.Lock();
   (a > 0 ? fooArray[b] : fooArray[c]).a = 0; // \
-    // expected-warning {{writing variable 'a' requires holding mutex '((a#_)#_#fooArray[b]).mu_' exclusively}} \
-    // expected-note {{'((a#_)#_#fooArray[_]).mu_'}}
+    // expected-warning {{writing variable 'a' requires holding mutex '((0 < a) ? fooArray[b] : fooArray[c]).mu_' exclusively}} \
+    // expected-note {{found near match '((0 < a) ? fooArray[1] : fooArray[b]).mu_'}}
   (a > 0 ? fooArray[1] : fooArray[b]).mu_.Unlock();
 }
 
@@ -4378,3 +4394,126 @@ class Foo {
 };
 
 }  // end namespace ThreadAttributesOnLambdas
+
+
+
+namespace AttributeExpressionCornerCases {
+
+class Foo {
+  int a GUARDED_BY(getMu());
+
+  Mutex* getMu()   LOCK_RETURNED("");
+  Mutex* getUniv() LOCK_RETURNED("*");
+
+  void test1() {
+    a = 0;
+  }
+
+  void test2() EXCLUSIVE_LOCKS_REQUIRED(getUniv()) {
+    a = 0;
+  }
+
+  void foo(Mutex* mu) EXCLUSIVE_LOCKS_REQUIRED(mu);
+
+  void test3() {
+    foo(nullptr);
+  }
+};
+
+
+class MapTest {
+  struct MuCell { Mutex* mu; };
+
+  MyMap<MyString, Mutex*> map;
+  MyMap<MyString, MuCell> mapCell;
+
+  int a GUARDED_BY(map["foo"]);
+  int b GUARDED_BY(mapCell["foo"].mu);
+
+  void test() {
+    map["foo"]->Lock();
+    a = 0;
+    map["foo"]->Unlock();
+  }
+
+  void test2() {
+    mapCell["foo"].mu->Lock();
+    b = 0;
+    mapCell["foo"].mu->Unlock();
+  }
+};
+
+
+class PreciseSmartPtr {
+  SmartPtr<Mutex> mu;
+  int val GUARDED_BY(mu);
+
+  static bool compare(PreciseSmartPtr& a, PreciseSmartPtr &b) {
+    a.mu->Lock();
+    bool result = (a.val == b.val);   // expected-warning {{reading variable 'val' requires holding mutex 'b.mu'}} \
+                                      // expected-note {{found near match 'a.mu'}}
+    a.mu->Unlock();
+    return result;
+  }
+};
+
+
+class SmartRedeclare {
+  SmartPtr<Mutex> mu;
+  int val GUARDED_BY(mu);
+
+  void test()  EXCLUSIVE_LOCKS_REQUIRED(mu);
+  void test2() EXCLUSIVE_LOCKS_REQUIRED(mu.get());
+  void test3() EXCLUSIVE_LOCKS_REQUIRED(mu.get());
+};
+
+
+void SmartRedeclare::test() EXCLUSIVE_LOCKS_REQUIRED(mu.get()) {
+  val = 0;
+}
+
+void SmartRedeclare::test2() EXCLUSIVE_LOCKS_REQUIRED(mu) {
+  val = 0;
+}
+
+void SmartRedeclare::test3() {
+  val = 0;
+}
+
+
+namespace CustomMutex {
+
+
+class LOCKABLE BaseMutex { };
+class DerivedMutex : public BaseMutex { };
+
+void customLock(const BaseMutex *m)   EXCLUSIVE_LOCK_FUNCTION(m);
+void customUnlock(const BaseMutex *m) UNLOCK_FUNCTION(m);
+
+static struct DerivedMutex custMu;
+
+static void doSomethingRequiringLock() EXCLUSIVE_LOCKS_REQUIRED(custMu) { }
+
+void customTest() {
+  customLock(reinterpret_cast<BaseMutex*>(&custMu));  // ignore casts
+  doSomethingRequiringLock();
+  customUnlock(reinterpret_cast<BaseMutex*>(&custMu));
+}
+
+} // end namespace CustomMutex
+
+} // end AttributeExpressionCornerCases
+
+
+namespace ScopedLockReturnedInvalid {
+
+class Opaque;
+
+Mutex* getMutex(Opaque* o) LOCK_RETURNED("");
+
+void test(Opaque* o) {
+  MutexLock lock(getMutex(o));
+}
+
+}  // end namespace ScopedLockReturnedInvalid
+





More information about the cfe-commits mailing list