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