<div dir="ltr">This commit instantiated compareByCase which emitted this warning under MSVC:<div><div>d:\src\llvm\tools\clang\include\clang\analysis\analyses\threadsafetytraverse.h(436) : warning C4715: 'clang::threadSafety::til::Comparator<clang::threadSafety::til::MatchComparator>::compareByCase' : not all control paths return a value</div>
</div><div><br></div><div>I added an unreachable after the switch in r214103 to fix it.</div></div><div class="gmail_extra"><br><br><div class="gmail_quote">On Mon, Jul 28, 2014 at 8:57 AM, DeLesley Hutchins <span dir="ltr"><<a href="mailto:delesley@google.com" target="_blank">delesley@google.com</a>></span> wrote:<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Author: delesley<br>
Date: Mon Jul 28 10:57:27 2014<br>
New Revision: 214089<br>
<br>
URL: <a href="http://llvm.org/viewvc/llvm-project?rev=214089&view=rev" target="_blank">http://llvm.org/viewvc/llvm-project?rev=214089&view=rev</a><br>
Log:<br>
Thread Safety Analysis: Replace the old and broken SExpr with the new<br>
til::SExpr. This is a large patch, with many small changes to pretty printing<br>
and expression lowering to make the new SExpr representation equivalent in<br>
functionality to the old.<br>
<br>
Modified:<br>
cfe/trunk/include/clang/Analysis/Analyses/ThreadSafety.h<br>
cfe/trunk/include/clang/Analysis/Analyses/ThreadSafetyCommon.h<br>
cfe/trunk/include/clang/Analysis/Analyses/ThreadSafetyTIL.h<br>
cfe/trunk/include/clang/Analysis/Analyses/ThreadSafetyTraverse.h<br>
cfe/trunk/include/clang/Analysis/Analyses/ThreadSafetyUtil.h<br>
cfe/trunk/lib/Analysis/ThreadSafety.cpp<br>
cfe/trunk/lib/Analysis/ThreadSafetyCommon.cpp<br>
cfe/trunk/lib/Analysis/ThreadSafetyTIL.cpp<br>
cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp<br>
cfe/trunk/test/SemaCXX/warn-thread-safety-analysis.cpp<br>
<br>
Modified: cfe/trunk/include/clang/Analysis/Analyses/ThreadSafety.h<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Analysis/Analyses/ThreadSafety.h?rev=214089&r1=214088&r2=214089&view=diff" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Analysis/Analyses/ThreadSafety.h?rev=214089&r1=214088&r2=214089&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/include/clang/Analysis/Analyses/ThreadSafety.h (original)<br>
+++ cfe/trunk/include/clang/Analysis/Analyses/ThreadSafety.h Mon Jul 28 10:57:27 2014<br>
@@ -24,7 +24,7 @@<br>
#include "llvm/ADT/StringRef.h"<br>
<br>
namespace clang {<br>
-namespace thread_safety {<br>
+namespace threadSafety {<br>
<br>
/// This enum distinguishes between different kinds of operations that may<br>
/// need to be protected by locks. We use this enum in error handling.<br>
@@ -190,5 +190,5 @@ void runThreadSafetyAnalysis(AnalysisDec<br>
/// of access.<br>
LockKind getLockKindFromAccessKind(AccessKind AK);<br>
<br>
-}} // end namespace clang::thread_safety<br>
+}} // end namespace clang::threadSafety<br>
#endif<br>
<br>
Modified: cfe/trunk/include/clang/Analysis/Analyses/ThreadSafetyCommon.h<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Analysis/Analyses/ThreadSafetyCommon.h?rev=214089&r1=214088&r2=214089&view=diff" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Analysis/Analyses/ThreadSafetyCommon.h?rev=214089&r1=214088&r2=214089&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/include/clang/Analysis/Analyses/ThreadSafetyCommon.h (original)<br>
+++ cfe/trunk/include/clang/Analysis/Analyses/ThreadSafetyCommon.h Mon Jul 28 10:57:27 2014<br>
@@ -219,18 +219,16 @@ public:<br>
/// should be evaluated; multiple calling contexts can be chained together<br>
/// by the lock_returned attribute.<br>
struct CallingContext {<br>
+ CallingContext *Prev; // The previous context; or 0 if none.<br>
const NamedDecl *AttrDecl; // The decl to which the attr is attached.<br>
const Expr *SelfArg; // Implicit object argument -- e.g. 'this'<br>
unsigned NumArgs; // Number of funArgs<br>
const Expr *const *FunArgs; // Function arguments<br>
- CallingContext *Prev; // The previous context; or 0 if none.<br>
bool SelfArrow; // is Self referred to with -> or .?<br>
<br>
- CallingContext(const NamedDecl *D = nullptr, const Expr *S = nullptr,<br>
- unsigned N = 0, const Expr *const *A = nullptr,<br>
- CallingContext *P = nullptr)<br>
- : AttrDecl(D), SelfArg(S), NumArgs(N), FunArgs(A), Prev(P),<br>
- SelfArrow(false)<br>
+ CallingContext(CallingContext *P, const NamedDecl *D = nullptr)<br>
+ : Prev(P), AttrDecl(D), SelfArg(nullptr),<br>
+ NumArgs(0), FunArgs(nullptr), SelfArrow(false)<br>
{}<br>
};<br>
<br>
@@ -242,6 +240,13 @@ public:<br>
SelfVar->setKind(til::Variable::VK_SFun);<br>
}<br>
<br>
+ // Translate a clang expression in an attribute to a til::SExpr.<br>
+ // Constructs the context from D, DeclExp, and SelfDecl.<br>
+ til::SExpr *translateAttrExpr(const Expr *AttrExp, const NamedDecl *D,<br>
+ const Expr *DeclExp, VarDecl *SelfDecl=nullptr);<br>
+<br>
+ til::SExpr *translateAttrExpr(const Expr *AttrExp, CallingContext *Ctx);<br>
+<br>
// Translate a clang statement or expression to a TIL expression.<br>
// Also performs substitution of variables; Ctx provides the context.<br>
// Dispatches on the type of S.<br>
@@ -262,7 +267,8 @@ private:<br>
CallingContext *Ctx) ;<br>
til::SExpr *translateCXXThisExpr(const CXXThisExpr *TE, CallingContext *Ctx);<br>
til::SExpr *translateMemberExpr(const MemberExpr *ME, CallingContext *Ctx);<br>
- til::SExpr *translateCallExpr(const CallExpr *CE, CallingContext *Ctx);<br>
+ til::SExpr *translateCallExpr(const CallExpr *CE, CallingContext *Ctx,<br>
+ const Expr *SelfE = nullptr);<br>
til::SExpr *translateCXXMemberCallExpr(const CXXMemberCallExpr *ME,<br>
CallingContext *Ctx);<br>
til::SExpr *translateCXXOperatorCallExpr(const CXXOperatorCallExpr *OCE,<br>
@@ -280,10 +286,8 @@ private:<br>
til::SExpr *translateCastExpr(const CastExpr *CE, CallingContext *Ctx);<br>
til::SExpr *translateArraySubscriptExpr(const ArraySubscriptExpr *E,<br>
CallingContext *Ctx);<br>
- til::SExpr *translateConditionalOperator(const ConditionalOperator *C,<br>
- CallingContext *Ctx);<br>
- til::SExpr *translateBinaryConditionalOperator(<br>
- const BinaryConditionalOperator *C, CallingContext *Ctx);<br>
+ til::SExpr *translateAbstractConditionalOperator(<br>
+ const AbstractConditionalOperator *C, CallingContext *Ctx);<br>
<br>
til::SExpr *translateDeclStmt(const DeclStmt *S, CallingContext *Ctx);<br>
<br>
@@ -362,16 +366,19 @@ private:<br>
void mergePhiNodesBackEdge(const CFGBlock *Blk);<br>
<br>
private:<br>
+ // Set to true when parsing capability expressions, which get translated<br>
+ // inaccurately in order to hack around smart pointers etc.<br>
+ static const bool CapabilityExprMode = true;<br>
+<br>
til::MemRegionRef Arena;<br>
til::Variable *SelfVar; // Variable to use for 'this'. May be null.<br>
- til::SCFG *Scfg;<br>
<br>
+ til::SCFG *Scfg;<br>
StatementMap SMap; // Map from Stmt to TIL Variables<br>
LVarIndexMap LVarIdxMap; // Indices of clang local vars.<br>
std::vector<til::BasicBlock *> BlockMap; // Map from clang to til BBs.<br>
std::vector<BlockInfo> BBInfo; // Extra information per BB.<br>
// Indexed by clang BlockID.<br>
- std::unique_ptr<SExprBuilder::CallingContext> CallCtx; // Root calling context<br>
<br>
LVarDefinitionMap CurrentLVarMap;<br>
std::vector<til::Variable*> CurrentArguments;<br>
<br>
Modified: cfe/trunk/include/clang/Analysis/Analyses/ThreadSafetyTIL.h<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Analysis/Analyses/ThreadSafetyTIL.h?rev=214089&r1=214088&r2=214089&view=diff" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Analysis/Analyses/ThreadSafetyTIL.h?rev=214089&r1=214088&r2=214089&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/include/clang/Analysis/Analyses/ThreadSafetyTIL.h (original)<br>
+++ cfe/trunk/include/clang/Analysis/Analyses/ThreadSafetyTIL.h Mon Jul 28 10:57:27 2014<br>
@@ -100,6 +100,7 @@ enum TIL_CastOpcode : unsigned char {<br>
CAST_truncNum, // truncate precision of numeric type<br>
CAST_toFloat, // convert to floating point type<br>
CAST_toInt, // convert to integer type<br>
+ CAST_objToPtr // convert smart pointer to pointer (C++ only)<br>
};<br>
<br>
const TIL_Opcode COP_Min = COP_Future;<br>
@@ -405,7 +406,8 @@ public:<br>
return Vs.reduceVariableRef(this);<br>
}<br>
<br>
- template <class C> typename C::CType compare(Variable* E, C& Cmp) {<br>
+ template <class C><br>
+ typename C::CType compare(const Variable* E, C& Cmp) const {<br>
return Cmp.compareVariableRefs(this, E);<br>
}<br>
<br>
@@ -455,7 +457,7 @@ public:<br>
virtual SExpr *create() { return nullptr; }<br>
<br>
// Return the result of this future if it exists, otherwise return null.<br>
- SExpr *maybeGetResult() {<br>
+ SExpr *maybeGetResult() const {<br>
return Result;<br>
}<br>
<br>
@@ -478,7 +480,8 @@ public:<br>
return Vs.traverse(Result, Ctx);<br>
}<br>
<br>
- template <class C> typename C::CType compare(Future* E, C& Cmp) {<br>
+ template <class C><br>
+ typename C::CType compare(const Future* E, C& Cmp) const {<br>
if (!Result || !E->Result)<br>
return Cmp.comparePointers(this, E);<br>
return Cmp.compare(Result, E->Result);<br>
@@ -572,8 +575,9 @@ public:<br>
return Vs.reduceUndefined(*this);<br>
}<br>
<br>
- template <class C> typename C::CType compare(Undefined* E, C& Cmp) {<br>
- return Cmp.comparePointers(Cstmt, E->Cstmt);<br>
+ template <class C><br>
+ typename C::CType compare(const Undefined* E, C& Cmp) const {<br>
+ return Cmp.trueResult();<br>
}<br>
<br>
private:<br>
@@ -593,7 +597,8 @@ public:<br>
return Vs.reduceWildcard(*this);<br>
}<br>
<br>
- template <class C> typename C::CType compare(Wildcard* E, C& Cmp) {<br>
+ template <class C><br>
+ typename C::CType compare(const Wildcard* E, C& Cmp) const {<br>
return Cmp.trueResult();<br>
}<br>
};<br>
@@ -626,9 +631,10 @@ public:<br>
<br>
template <class V> typename V::R_SExpr traverse(V &Vs, typename V::R_Ctx Ctx);<br>
<br>
- template <class C> typename C::CType compare(Literal* E, C& Cmp) {<br>
- // TODO -- use value, not pointer equality<br>
- return Cmp.comparePointers(Cexpr, E->Cexpr);<br>
+ template <class C><br>
+ typename C::CType compare(const Literal* E, C& Cmp) const {<br>
+ // TODO: defer actual comparison to LiteralT<br>
+ return Cmp.trueResult();<br>
}<br>
<br>
private:<br>
@@ -727,7 +733,8 @@ public:<br>
return Vs.reduceLiteralPtr(*this);<br>
}<br>
<br>
- template <class C> typename C::CType compare(LiteralPtr* E, C& Cmp) {<br>
+ template <class C><br>
+ typename C::CType compare(const LiteralPtr* E, C& Cmp) const {<br>
return Cmp.comparePointers(Cvdecl, E->Cvdecl);<br>
}<br>
<br>
@@ -769,7 +776,8 @@ public:<br>
return Vs.reduceFunction(*this, Nvd, E1);<br>
}<br>
<br>
- template <class C> typename C::CType compare(Function* E, C& Cmp) {<br>
+ template <class C><br>
+ typename C::CType compare(const Function* E, C& Cmp) const {<br>
typename C::CType Ct =<br>
Cmp.compare(VarDecl->definition(), E->VarDecl->definition());<br>
if (Cmp.notTrue(Ct))<br>
@@ -824,7 +832,8 @@ public:<br>
return Vs.reduceSFunction(*this, Nvd, E1);<br>
}<br>
<br>
- template <class C> typename C::CType compare(SFunction* E, C& Cmp) {<br>
+ template <class C><br>
+ typename C::CType compare(const SFunction* E, C& Cmp) const {<br>
Cmp.enterScope(variableDecl(), E->variableDecl());<br>
typename C::CType Ct = Cmp.compare(body(), E->body());<br>
Cmp.leaveScope();<br>
@@ -859,7 +868,8 @@ public:<br>
return Vs.reduceCode(*this, Nt, Nb);<br>
}<br>
<br>
- template <class C> typename C::CType compare(Code* E, C& Cmp) {<br>
+ template <class C><br>
+ typename C::CType compare(const Code* E, C& Cmp) const {<br>
typename C::CType Ct = Cmp.compare(returnType(), E->returnType());<br>
if (Cmp.notTrue(Ct))<br>
return Ct;<br>
@@ -894,7 +904,8 @@ public:<br>
return Vs.reduceField(*this, Nr, Nb);<br>
}<br>
<br>
- template <class C> typename C::CType compare(Field* E, C& Cmp) {<br>
+ template <class C><br>
+ typename C::CType compare(const Field* E, C& Cmp) const {<br>
typename C::CType Ct = Cmp.compare(range(), E->range());<br>
if (Cmp.notTrue(Ct))<br>
return Ct;<br>
@@ -930,7 +941,8 @@ public:<br>
return Vs.reduceApply(*this, Nf, Na);<br>
}<br>
<br>
- template <class C> typename C::CType compare(Apply* E, C& Cmp) {<br>
+ template <class C><br>
+ typename C::CType compare(const Apply* E, C& Cmp) const {<br>
typename C::CType Ct = Cmp.compare(fun(), E->fun());<br>
if (Cmp.notTrue(Ct))<br>
return Ct;<br>
@@ -958,7 +970,7 @@ public:<br>
SExpr *arg() { return Arg.get() ? Arg.get() : Sfun.get(); }<br>
const SExpr *arg() const { return Arg.get() ? Arg.get() : Sfun.get(); }<br>
<br>
- bool isDelegation() const { return Arg == nullptr; }<br>
+ bool isDelegation() const { return Arg != nullptr; }<br>
<br>
template <class V><br>
typename V::R_SExpr traverse(V &Vs, typename V::R_Ctx Ctx) {<br>
@@ -968,7 +980,8 @@ public:<br>
return Vs.reduceSApply(*this, Nf, Na);<br>
}<br>
<br>
- template <class C> typename C::CType compare(SApply* E, C& Cmp) {<br>
+ template <class C><br>
+ typename C::CType compare(const SApply* E, C& Cmp) const {<br>
typename C::CType Ct = Cmp.compare(sfun(), E->sfun());<br>
if (Cmp.notTrue(Ct) || (!arg() && !E->arg()))<br>
return Ct;<br>
@@ -989,7 +1002,7 @@ public:<br>
Project(SExpr *R, StringRef SName)<br>
: SExpr(COP_Project), Rec(R), SlotName(SName), Cvdecl(nullptr)<br>
{ }<br>
- Project(SExpr *R, clang::ValueDecl *Cvd)<br>
+ Project(SExpr *R, const clang::ValueDecl *Cvd)<br>
: SExpr(COP_Project), Rec(R), SlotName(Cvd->getName()), Cvdecl(Cvd)<br>
{ }<br>
Project(const Project &P, SExpr *R)<br>
@@ -999,7 +1012,13 @@ public:<br>
SExpr *record() { return Rec.get(); }<br>
const SExpr *record() const { return Rec.get(); }<br>
<br>
- const clang::ValueDecl *clangValueDecl() const { return Cvdecl; }<br>
+ const clang::ValueDecl *clangDecl() const { return Cvdecl; }<br>
+<br>
+ bool isArrow() const { return (Flags & 0x01) != 0; }<br>
+ void setArrow(bool b) {<br>
+ if (b) Flags |= 0x01;<br>
+ else Flags &= 0xFFFE;<br>
+ }<br>
<br>
StringRef slotName() const {<br>
if (Cvdecl)<br>
@@ -1014,7 +1033,8 @@ public:<br>
return Vs.reduceProject(*this, Nr);<br>
}<br>
<br>
- template <class C> typename C::CType compare(Project* E, C& Cmp) {<br>
+ template <class C><br>
+ typename C::CType compare(const Project* E, C& Cmp) const {<br>
typename C::CType Ct = Cmp.compare(record(), E->record());<br>
if (Cmp.notTrue(Ct))<br>
return Ct;<br>
@@ -1024,7 +1044,7 @@ public:<br>
private:<br>
SExprRef Rec;<br>
StringRef SlotName;<br>
- clang::ValueDecl *Cvdecl;<br>
+ const clang::ValueDecl *Cvdecl;<br>
};<br>
<br>
<br>
@@ -1048,7 +1068,8 @@ public:<br>
return Vs.reduceCall(*this, Nt);<br>
}<br>
<br>
- template <class C> typename C::CType compare(Call* E, C& Cmp) {<br>
+ template <class C><br>
+ typename C::CType compare(const Call* E, C& Cmp) const {<br>
return Cmp.compare(target(), E->target());<br>
}<br>
<br>
@@ -1082,7 +1103,8 @@ public:<br>
return Vs.reduceAlloc(*this, Nd);<br>
}<br>
<br>
- template <class C> typename C::CType compare(Alloc* E, C& Cmp) {<br>
+ template <class C><br>
+ typename C::CType compare(const Alloc* E, C& Cmp) const {<br>
typename C::CType Ct = Cmp.compareIntegers(kind(), E->kind());<br>
if (Cmp.notTrue(Ct))<br>
return Ct;<br>
@@ -1111,7 +1133,8 @@ public:<br>
return Vs.reduceLoad(*this, Np);<br>
}<br>
<br>
- template <class C> typename C::CType compare(Load* E, C& Cmp) {<br>
+ template <class C><br>
+ typename C::CType compare(const Load* E, C& Cmp) const {<br>
return Cmp.compare(pointer(), E->pointer());<br>
}<br>
<br>
@@ -1142,7 +1165,8 @@ public:<br>
return Vs.reduceStore(*this, Np, Nv);<br>
}<br>
<br>
- template <class C> typename C::CType compare(Store* E, C& Cmp) {<br>
+ template <class C><br>
+ typename C::CType compare(const Store* E, C& Cmp) const {<br>
typename C::CType Ct = Cmp.compare(destination(), E->destination());<br>
if (Cmp.notTrue(Ct))<br>
return Ct;<br>
@@ -1178,7 +1202,8 @@ public:<br>
return Vs.reduceArrayIndex(*this, Na, Ni);<br>
}<br>
<br>
- template <class C> typename C::CType compare(ArrayIndex* E, C& Cmp) {<br>
+ template <class C><br>
+ typename C::CType compare(const ArrayIndex* E, C& Cmp) const {<br>
typename C::CType Ct = Cmp.compare(array(), E->array());<br>
if (Cmp.notTrue(Ct))<br>
return Ct;<br>
@@ -1215,7 +1240,8 @@ public:<br>
return Vs.reduceArrayAdd(*this, Na, Ni);<br>
}<br>
<br>
- template <class C> typename C::CType compare(ArrayAdd* E, C& Cmp) {<br>
+ template <class C><br>
+ typename C::CType compare(const ArrayAdd* E, C& Cmp) const {<br>
typename C::CType Ct = Cmp.compare(array(), E->array());<br>
if (Cmp.notTrue(Ct))<br>
return Ct;<br>
@@ -1251,7 +1277,8 @@ public:<br>
return Vs.reduceUnaryOp(*this, Ne);<br>
}<br>
<br>
- template <class C> typename C::CType compare(UnaryOp* E, C& Cmp) {<br>
+ template <class C><br>
+ typename C::CType compare(const UnaryOp* E, C& Cmp) const {<br>
typename C::CType Ct =<br>
Cmp.compareIntegers(unaryOpcode(), E->unaryOpcode());<br>
if (Cmp.notTrue(Ct))<br>
@@ -1295,7 +1322,8 @@ public:<br>
return Vs.reduceBinaryOp(*this, Ne0, Ne1);<br>
}<br>
<br>
- template <class C> typename C::CType compare(BinaryOp* E, C& Cmp) {<br>
+ template <class C><br>
+ typename C::CType compare(const BinaryOp* E, C& Cmp) const {<br>
typename C::CType Ct =<br>
Cmp.compareIntegers(binaryOpcode(), E->binaryOpcode());<br>
if (Cmp.notTrue(Ct))<br>
@@ -1333,7 +1361,8 @@ public:<br>
return Vs.reduceCast(*this, Ne);<br>
}<br>
<br>
- template <class C> typename C::CType compare(Cast* E, C& Cmp) {<br>
+ template <class C><br>
+ typename C::CType compare(const Cast* E, C& Cmp) const {<br>
typename C::CType Ct =<br>
Cmp.compareIntegers(castOpcode(), E->castOpcode());<br>
if (Cmp.notTrue(Ct))<br>
@@ -1386,7 +1415,8 @@ public:<br>
return Vs.reducePhi(*this, Nvs);<br>
}<br>
<br>
- template <class C> typename C::CType compare(Phi *E, C &Cmp) {<br>
+ template <class C><br>
+ typename C::CType compare(const Phi *E, C &Cmp) const {<br>
// TODO: implement CFG comparisons<br>
return Cmp.comparePointers(this, E);<br>
}<br>
@@ -1503,7 +1533,8 @@ public:<br>
return Vs.reduceBasicBlock(*this, Nas, Nis, Nt);<br>
}<br>
<br>
- template <class C> typename C::CType compare(BasicBlock *E, C &Cmp) {<br>
+ template <class C><br>
+ typename C::CType compare(const BasicBlock *E, C &Cmp) const {<br>
// TODO: implement CFG comparisons<br>
return Cmp.comparePointers(this, E);<br>
}<br>
@@ -1590,7 +1621,8 @@ public:<br>
return Vs.reduceSCFG(*this, Bbs);<br>
}<br>
<br>
- template <class C> typename C::CType compare(SCFG *E, C &Cmp) {<br>
+ template <class C><br>
+ typename C::CType compare(const SCFG *E, C &Cmp) const {<br>
// TODO -- implement CFG comparisons<br>
return Cmp.comparePointers(this, E);<br>
}<br>
@@ -1623,7 +1655,8 @@ public:<br>
return Vs.reduceGoto(*this, Ntb);<br>
}<br>
<br>
- template <class C> typename C::CType compare(Goto *E, C &Cmp) {<br>
+ template <class C><br>
+ typename C::CType compare(const Goto *E, C &Cmp) const {<br>
// TODO -- implement CFG comparisons<br>
return Cmp.comparePointers(this, E);<br>
}<br>
@@ -1668,7 +1701,8 @@ public:<br>
return Vs.reduceBranch(*this, Nc, Ntb, Nte);<br>
}<br>
<br>
- template <class C> typename C::CType compare(Branch *E, C &Cmp) {<br>
+ template <class C><br>
+ typename C::CType compare(const Branch *E, C &Cmp) const {<br>
// TODO -- implement CFG comparisons<br>
return Cmp.comparePointers(this, E);<br>
}<br>
@@ -1698,7 +1732,8 @@ public:<br>
return Vs.reduceIdentifier(*this);<br>
}<br>
<br>
- template <class C> typename C::CType compare(Identifier* E, C& Cmp) {<br>
+ template <class C><br>
+ typename C::CType compare(const Identifier* E, C& Cmp) const {<br>
return Cmp.compareStrings(name(), E->name());<br>
}<br>
<br>
@@ -1737,7 +1772,8 @@ public:<br>
return Vs.reduceIfThenElse(*this, Nc, Nt, Ne);<br>
}<br>
<br>
- template <class C> typename C::CType compare(IfThenElse* E, C& Cmp) {<br>
+ template <class C><br>
+ typename C::CType compare(const IfThenElse* E, C& Cmp) const {<br>
typename C::CType Ct = Cmp.compare(condition(), E->condition());<br>
if (Cmp.notTrue(Ct))<br>
return Ct;<br>
@@ -1784,7 +1820,8 @@ public:<br>
return Vs.reduceLet(*this, Nvd, E1);<br>
}<br>
<br>
- template <class C> typename C::CType compare(Let* E, C& Cmp) {<br>
+ template <class C><br>
+ typename C::CType compare(const Let* E, C& Cmp) const {<br>
typename C::CType Ct =<br>
Cmp.compare(VarDecl->definition(), E->VarDecl->definition());<br>
if (Cmp.notTrue(Ct))<br>
@@ -1802,7 +1839,8 @@ private:<br>
<br>
<br>
<br>
-SExpr *getCanonicalVal(SExpr *E);<br>
+const SExpr *getCanonicalVal(const SExpr *E);<br>
+SExpr* simplifyToCanonicalVal(SExpr *E);<br>
void simplifyIncompleteArg(Variable *V, til::Phi *Ph);<br>
<br>
<br>
<br>
Modified: cfe/trunk/include/clang/Analysis/Analyses/ThreadSafetyTraverse.h<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Analysis/Analyses/ThreadSafetyTraverse.h?rev=214089&r1=214088&r2=214089&view=diff" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Analysis/Analyses/ThreadSafetyTraverse.h?rev=214089&r1=214088&r2=214089&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/include/clang/Analysis/Analyses/ThreadSafetyTraverse.h (original)<br>
+++ cfe/trunk/include/clang/Analysis/Analyses/ThreadSafetyTraverse.h Mon Jul 28 10:57:27 2014<br>
@@ -19,6 +19,8 @@<br>
<br>
#include "ThreadSafetyTIL.h"<br>
<br>
+#include <ostream><br>
+<br>
namespace clang {<br>
namespace threadSafety {<br>
namespace til {<br>
@@ -423,7 +425,7 @@ protected:<br>
Self *self() { return reinterpret_cast<Self *>(this); }<br>
<br>
public:<br>
- bool compareByCase(SExpr *E1, SExpr* E2) {<br>
+ bool compareByCase(const SExpr *E1, const SExpr* E2) {<br>
switch (E1->opcode()) {<br>
#define TIL_OPCODE_DEF(X) \<br>
case COP_##X: \<br>
@@ -449,38 +451,86 @@ public:<br>
bool compareStrings (StringRef s, StringRef r) { return s == r; }<br>
bool comparePointers(const void* P, const void* Q) { return P == Q; }<br>
<br>
- bool compare(SExpr *E1, SExpr* E2) {<br>
+ bool compare(const SExpr *E1, const SExpr* E2) {<br>
if (E1->opcode() != E2->opcode())<br>
return false;<br>
return compareByCase(E1, E2);<br>
}<br>
<br>
// TODO -- handle alpha-renaming of variables<br>
- void enterScope(Variable* V1, Variable* V2) { }<br>
+ void enterScope(const Variable* V1, const Variable* V2) { }<br>
void leaveScope() { }<br>
<br>
- bool compareVariableRefs(Variable* V1, Variable* V2) {<br>
+ bool compareVariableRefs(const Variable* V1, const Variable* V2) {<br>
return V1 == V2;<br>
}<br>
<br>
- static bool compareExprs(SExpr *E1, SExpr* E2) {<br>
+ static bool compareExprs(const SExpr *E1, const SExpr* E2) {<br>
EqualsComparator Eq;<br>
return Eq.compare(E1, E2);<br>
}<br>
};<br>
<br>
<br>
+<br>
+class MatchComparator : public Comparator<MatchComparator> {<br>
+public:<br>
+ // Result type for the comparison, e.g. bool for simple equality,<br>
+ // or int for lexigraphic comparison (-1, 0, 1). Must have one value which<br>
+ // denotes "true".<br>
+ typedef bool CType;<br>
+<br>
+ CType trueResult() { return true; }<br>
+ bool notTrue(CType ct) { return !ct; }<br>
+<br>
+ bool compareIntegers(unsigned i, unsigned j) { return i == j; }<br>
+ bool compareStrings (StringRef s, StringRef r) { return s == r; }<br>
+ bool comparePointers(const void* P, const void* Q) { return P == Q; }<br>
+<br>
+ bool compare(const SExpr *E1, const SExpr* E2) {<br>
+ // Wildcards match anything.<br>
+ if (E1->opcode() == COP_Wildcard || E2->opcode() == COP_Wildcard)<br>
+ return true;<br>
+ // otherwise normal equality.<br>
+ if (E1->opcode() != E2->opcode())<br>
+ return false;<br>
+ return compareByCase(E1, E2);<br>
+ }<br>
+<br>
+ // TODO -- handle alpha-renaming of variables<br>
+ void enterScope(const Variable* V1, const Variable* V2) { }<br>
+ void leaveScope() { }<br>
+<br>
+ bool compareVariableRefs(const Variable* V1, const Variable* V2) {<br>
+ return V1 == V2;<br>
+ }<br>
+<br>
+ static bool compareExprs(const SExpr *E1, const SExpr* E2) {<br>
+ MatchComparator Matcher;<br>
+ return Matcher.compare(E1, E2);<br>
+ }<br>
+};<br>
+<br>
+<br>
+<br>
+inline std::ostream& operator<<(std::ostream& SS, llvm::StringRef R) {<br>
+ return SS.write(R.data(), R.size());<br>
+}<br>
+<br>
// Pretty printer for TIL expressions<br>
template <typename Self, typename StreamType><br>
class PrettyPrinter {<br>
private:<br>
bool Verbose; // Print out additional information<br>
bool Cleanup; // Omit redundant decls.<br>
+ bool CStyle; // Print exprs in C-like syntax.<br>
<br>
public:<br>
- PrettyPrinter(bool V = false, bool C = true) : Verbose(V), Cleanup(C) { }<br>
+ PrettyPrinter(bool V = false, bool C = true, bool CS = true)<br>
+ : Verbose(V), Cleanup(C), CStyle(CS)<br>
+ {}<br>
<br>
- static void print(SExpr *E, StreamType &SS) {<br>
+ static void print(const SExpr *E, StreamType &SS) {<br>
Self printer;<br>
printer.printSExpr(E, SS, Prec_MAX);<br>
}<br>
@@ -502,7 +552,7 @@ protected:<br>
static const unsigned Prec_MAX = 6;<br>
<br>
// Return the precedence of a given node, for use in pretty printing.<br>
- unsigned precedence(SExpr *E) {<br>
+ unsigned precedence(const SExpr *E) {<br>
switch (E->opcode()) {<br>
case COP_Future: return Prec_Atom;<br>
case COP_Undefined: return Prec_Atom;<br>
@@ -529,7 +579,7 @@ protected:<br>
<br>
case COP_UnaryOp: return Prec_Unary;<br>
case COP_BinaryOp: return Prec_Binary;<br>
- case COP_Cast: return Prec_Unary;<br>
+ case COP_Cast: return Prec_Atom;<br>
<br>
case COP_SCFG: return Prec_Decl;<br>
case COP_BasicBlock: return Prec_MAX;<br>
@@ -544,7 +594,7 @@ protected:<br>
return Prec_MAX;<br>
}<br>
<br>
- void printBlockLabel(StreamType & SS, BasicBlock *BB, unsigned index) {<br>
+ void printBlockLabel(StreamType & SS, const BasicBlock *BB, unsigned index) {<br>
if (!BB) {<br>
SS << "BB_null";<br>
return;<br>
@@ -555,7 +605,7 @@ protected:<br>
SS << index;<br>
}<br>
<br>
- void printSExpr(SExpr *E, StreamType &SS, unsigned P) {<br>
+ void printSExpr(const SExpr *E, StreamType &SS, unsigned P) {<br>
if (!E) {<br>
self()->printNull(SS);<br>
return;<br>
@@ -582,28 +632,28 @@ protected:<br>
SS << "#null";<br>
}<br>
<br>
- void printFuture(Future *E, StreamType &SS) {<br>
+ void printFuture(const Future *E, StreamType &SS) {<br>
self()->printSExpr(E->maybeGetResult(), SS, Prec_Atom);<br>
}<br>
<br>
- void printUndefined(Undefined *E, StreamType &SS) {<br>
+ void printUndefined(const Undefined *E, StreamType &SS) {<br>
SS << "#undefined";<br>
}<br>
<br>
- void printWildcard(Wildcard *E, StreamType &SS) {<br>
- SS << "_";<br>
+ void printWildcard(const Wildcard *E, StreamType &SS) {<br>
+ SS << "*";<br>
}<br>
<br>
template<class T><br>
- void printLiteralT(LiteralT<T> *E, StreamType &SS) {<br>
+ void printLiteralT(const LiteralT<T> *E, StreamType &SS) {<br>
SS << E->value();<br>
}<br>
<br>
- void printLiteralT(LiteralT<uint8_t> *E, StreamType &SS) {<br>
+ void printLiteralT(const LiteralT<uint8_t> *E, StreamType &SS) {<br>
SS << "'" << E->value() << "'";<br>
}<br>
<br>
- void printLiteral(Literal *E, StreamType &SS) {<br>
+ void printLiteral(const Literal *E, StreamType &SS) {<br>
if (E->clangExpr()) {<br>
SS << getSourceLiteralString(E->clangExpr());<br>
return;<br>
@@ -685,13 +735,13 @@ protected:<br>
SS << "#lit";<br>
}<br>
<br>
- void printLiteralPtr(LiteralPtr *E, StreamType &SS) {<br>
+ void printLiteralPtr(const LiteralPtr *E, StreamType &SS) {<br>
SS << E->clangDecl()->getNameAsString();<br>
}<br>
<br>
- void printVariable(Variable *V, StreamType &SS, bool IsVarDecl = false) {<br>
+ void printVariable(const Variable *V, StreamType &SS, bool IsVarDecl = false) {<br>
if (!IsVarDecl && Cleanup) {<br>
- SExpr* E = getCanonicalVal(V);<br>
+ const SExpr* E = getCanonicalVal(V);<br>
if (E != V) {<br>
printSExpr(E, SS, Prec_Atom);<br>
return;<br>
@@ -699,11 +749,13 @@ protected:<br>
}<br>
if (V->kind() == Variable::VK_LetBB)<br>
SS << V->name() << V->getBlockID() << "_" << V->getID();<br>
+ else if (CStyle && V->kind() == Variable::VK_SFun)<br>
+ SS << "this";<br>
else<br>
SS << V->name() << V->getID();<br>
}<br>
<br>
- void printFunction(Function *E, StreamType &SS, unsigned sugared = 0) {<br>
+ void printFunction(const Function *E, StreamType &SS, unsigned sugared = 0) {<br>
switch (sugared) {<br>
default:<br>
SS << "\\("; // Lambda<br>
@@ -719,7 +771,7 @@ protected:<br>
SS << ": ";<br>
self()->printSExpr(E->variableDecl()->definition(), SS, Prec_MAX);<br>
<br>
- SExpr *B = E->body();<br>
+ const SExpr *B = E->body();<br>
if (B && B->opcode() == COP_Function)<br>
self()->printFunction(cast<Function>(B), SS, 2);<br>
else {<br>
@@ -728,29 +780,29 @@ protected:<br>
}<br>
}<br>
<br>
- void printSFunction(SFunction *E, StreamType &SS) {<br>
+ void printSFunction(const SFunction *E, StreamType &SS) {<br>
SS << "@";<br>
self()->printVariable(E->variableDecl(), SS, true);<br>
SS << " ";<br>
self()->printSExpr(E->body(), SS, Prec_Decl);<br>
}<br>
<br>
- void printCode(Code *E, StreamType &SS) {<br>
+ void printCode(const Code *E, StreamType &SS) {<br>
SS << ": ";<br>
self()->printSExpr(E->returnType(), SS, Prec_Decl-1);<br>
SS << " -> ";<br>
self()->printSExpr(E->body(), SS, Prec_Decl);<br>
}<br>
<br>
- void printField(Field *E, StreamType &SS) {<br>
+ void printField(const Field *E, StreamType &SS) {<br>
SS << ": ";<br>
self()->printSExpr(E->range(), SS, Prec_Decl-1);<br>
SS << " = ";<br>
self()->printSExpr(E->body(), SS, Prec_Decl);<br>
}<br>
<br>
- void printApply(Apply *E, StreamType &SS, bool sugared = false) {<br>
- SExpr *F = E->fun();<br>
+ void printApply(const Apply *E, StreamType &SS, bool sugared = false) {<br>
+ const SExpr *F = E->fun();<br>
if (F->opcode() == COP_Apply) {<br>
printApply(cast<Apply>(F), SS, true);<br>
SS << ", ";<br>
@@ -763,7 +815,7 @@ protected:<br>
SS << ")$";<br>
}<br>
<br>
- void printSApply(SApply *E, StreamType &SS) {<br>
+ void printSApply(const SApply *E, StreamType &SS) {<br>
self()->printSExpr(E->sfun(), SS, Prec_Postfix);<br>
if (E->isDelegation()) {<br>
SS << "@(";<br>
@@ -772,14 +824,36 @@ protected:<br>
}<br>
}<br>
<br>
- void printProject(Project *E, StreamType &SS) {<br>
+ void printProject(const Project *E, StreamType &SS) {<br>
+ if (CStyle) {<br>
+ // Omit the this-><br>
+ if (const SApply *SAP = dyn_cast<SApply>(E->record())) {<br>
+ if (const Variable *V = dyn_cast<Variable>(SAP->sfun())) {<br>
+ if (!SAP->isDelegation() && V->kind() == Variable::VK_SFun) {<br>
+ SS << E->slotName();<br>
+ return;<br>
+ }<br>
+ }<br>
+ }<br>
+ if (isa<Wildcard>(E->record())) {<br>
+ // handle existentials<br>
+ SS << "&";<br>
+ SS << E->clangDecl()->getQualifiedNameAsString();<br>
+ return;<br>
+ }<br>
+ }<br>
self()->printSExpr(E->record(), SS, Prec_Postfix);<br>
- SS << ".";<br>
+ if (CStyle && E->isArrow()) {<br>
+ SS << "->";<br>
+ }<br>
+ else {<br>
+ SS << ".";<br>
+ }<br>
SS << E->slotName();<br>
}<br>
<br>
- void printCall(Call *E, StreamType &SS) {<br>
- SExpr *T = E->target();<br>
+ void printCall(const Call *E, StreamType &SS) {<br>
+ const SExpr *T = E->target();<br>
if (T->opcode() == COP_Apply) {<br>
self()->printApply(cast<Apply>(T), SS, true);<br>
SS << ")";<br>
@@ -790,52 +864,60 @@ protected:<br>
}<br>
}<br>
<br>
- void printAlloc(Alloc *E, StreamType &SS) {<br>
+ void printAlloc(const Alloc *E, StreamType &SS) {<br>
SS << "new ";<br>
self()->printSExpr(E->dataType(), SS, Prec_Other-1);<br>
}<br>
<br>
- void printLoad(Load *E, StreamType &SS) {<br>
+ void printLoad(const Load *E, StreamType &SS) {<br>
self()->printSExpr(E->pointer(), SS, Prec_Postfix);<br>
- SS << "^";<br>
+ if (!CStyle)<br>
+ SS << "^";<br>
}<br>
<br>
- void printStore(Store *E, StreamType &SS) {<br>
+ void printStore(const Store *E, StreamType &SS) {<br>
self()->printSExpr(E->destination(), SS, Prec_Other-1);<br>
SS << " := ";<br>
self()->printSExpr(E->source(), SS, Prec_Other-1);<br>
}<br>
<br>
- void printArrayIndex(ArrayIndex *E, StreamType &SS) {<br>
+ void printArrayIndex(const ArrayIndex *E, StreamType &SS) {<br>
self()->printSExpr(E->array(), SS, Prec_Postfix);<br>
SS << "[";<br>
self()->printSExpr(E->index(), SS, Prec_MAX);<br>
SS << "]";<br>
}<br>
<br>
- void printArrayAdd(ArrayAdd *E, StreamType &SS) {<br>
+ void printArrayAdd(const ArrayAdd *E, StreamType &SS) {<br>
self()->printSExpr(E->array(), SS, Prec_Postfix);<br>
SS << " + ";<br>
self()->printSExpr(E->index(), SS, Prec_Atom);<br>
}<br>
<br>
- void printUnaryOp(UnaryOp *E, StreamType &SS) {<br>
+ void printUnaryOp(const UnaryOp *E, StreamType &SS) {<br>
SS << getUnaryOpcodeString(E->unaryOpcode());<br>
self()->printSExpr(E->expr(), SS, Prec_Unary);<br>
}<br>
<br>
- void printBinaryOp(BinaryOp *E, StreamType &SS) {<br>
+ void printBinaryOp(const BinaryOp *E, StreamType &SS) {<br>
self()->printSExpr(E->expr0(), SS, Prec_Binary-1);<br>
SS << " " << getBinaryOpcodeString(E->binaryOpcode()) << " ";<br>
self()->printSExpr(E->expr1(), SS, Prec_Binary-1);<br>
}<br>
<br>
- void printCast(Cast *E, StreamType &SS) {<br>
- SS << "%";<br>
+ void printCast(const Cast *E, StreamType &SS) {<br>
+ if (!CStyle) {<br>
+ SS << "cast[";<br>
+ SS << E->castOpcode();<br>
+ SS << "](";<br>
+ self()->printSExpr(E->expr(), SS, Prec_Unary);<br>
+ SS << ")";<br>
+ return;<br>
+ }<br>
self()->printSExpr(E->expr(), SS, Prec_Unary);<br>
}<br>
<br>
- void printSCFG(SCFG *E, StreamType &SS) {<br>
+ void printSCFG(const SCFG *E, StreamType &SS) {<br>
SS << "CFG {\n";<br>
for (auto BBI : *E) {<br>
printBasicBlock(BBI, SS);<br>
@@ -844,7 +926,7 @@ protected:<br>
newline(SS);<br>
}<br>
<br>
- void printBasicBlock(BasicBlock *E, StreamType &SS) {<br>
+ void printBasicBlock(const BasicBlock *E, StreamType &SS) {<br>
SS << "BB_" << E->blockID() << ":";<br>
if (E->parent())<br>
SS << " BB_" << E->parent()->blockID();<br>
@@ -867,7 +949,7 @@ protected:<br>
SS << ";";<br>
newline(SS);<br>
}<br>
- SExpr *T = E->terminator();<br>
+ const SExpr *T = E->terminator();<br>
if (T) {<br>
self()->printSExpr(T, SS, Prec_MAX);<br>
SS << ";";<br>
@@ -876,7 +958,7 @@ protected:<br>
newline(SS);<br>
}<br>
<br>
- void printPhi(Phi *E, StreamType &SS) {<br>
+ void printPhi(const Phi *E, StreamType &SS) {<br>
SS << "phi(";<br>
if (E->status() == Phi::PH_SingleVal)<br>
self()->printSExpr(E->values()[0], SS, Prec_MAX);<br>
@@ -891,12 +973,12 @@ protected:<br>
SS << ")";<br>
}<br>
<br>
- void printGoto(Goto *E, StreamType &SS) {<br>
+ void printGoto(const Goto *E, StreamType &SS) {<br>
SS << "goto ";<br>
printBlockLabel(SS, E->targetBlock(), E->index());<br>
}<br>
<br>
- void printBranch(Branch *E, StreamType &SS) {<br>
+ void printBranch(const Branch *E, StreamType &SS) {<br>
SS << "branch (";<br>
self()->printSExpr(E->condition(), SS, Prec_MAX);<br>
SS << ") ";<br>
@@ -905,11 +987,19 @@ protected:<br>
printBlockLabel(SS, E->elseBlock(), E->elseIndex());<br>
}<br>
<br>
- void printIdentifier(Identifier *E, StreamType &SS) {<br>
+ void printIdentifier(const Identifier *E, StreamType &SS) {<br>
SS << E->name();<br>
}<br>
<br>
- void printIfThenElse(IfThenElse *E, StreamType &SS) {<br>
+ void printIfThenElse(const IfThenElse *E, StreamType &SS) {<br>
+ if (CStyle) {<br>
+ printSExpr(E->condition(), SS, Prec_Unary);<br>
+ SS << " ? ";<br>
+ printSExpr(E->thenExpr(), SS, Prec_Unary);<br>
+ SS << " : ";<br>
+ printSExpr(E->elseExpr(), SS, Prec_Unary);<br>
+ return;<br>
+ }<br>
SS << "if (";<br>
printSExpr(E->condition(), SS, Prec_MAX);<br>
SS << ") then ";<br>
@@ -918,7 +1008,7 @@ protected:<br>
printSExpr(E->elseExpr(), SS, Prec_Other);<br>
}<br>
<br>
- void printLet(Let *E, StreamType &SS) {<br>
+ void printLet(const Let *E, StreamType &SS) {<br>
SS << "let ";<br>
printVariable(E->variableDecl(), SS, true);<br>
SS << " = ";<br>
@@ -929,6 +1019,10 @@ protected:<br>
};<br>
<br>
<br>
+class StdPrinter : public PrettyPrinter<StdPrinter, std::ostream> { };<br>
+<br>
+<br>
+<br>
} // end namespace til<br>
} // end namespace threadSafety<br>
} // end namespace clang<br>
<br>
Modified: cfe/trunk/include/clang/Analysis/Analyses/ThreadSafetyUtil.h<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Analysis/Analyses/ThreadSafetyUtil.h?rev=214089&r1=214088&r2=214089&view=diff" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Analysis/Analyses/ThreadSafetyUtil.h?rev=214089&r1=214088&r2=214089&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/include/clang/Analysis/Analyses/ThreadSafetyUtil.h (original)<br>
+++ cfe/trunk/include/clang/Analysis/Analyses/ThreadSafetyUtil.h Mon Jul 28 10:57:27 2014<br>
@@ -144,7 +144,9 @@ public:<br>
}<br>
<br>
iterator begin() { return Data; }<br>
+ const_iterator begin() const { return Data; }<br>
iterator end() { return Data + Size; }<br>
+ const_iterator end() const { return Data + Size; }<br>
<br>
const_iterator cbegin() const { return Data; }<br>
const_iterator cend() const { return Data + Size; }<br>
<br>
Modified: cfe/trunk/lib/Analysis/ThreadSafety.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/ThreadSafety.cpp?rev=214089&r1=214088&r2=214089&view=diff" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/ThreadSafety.cpp?rev=214089&r1=214088&r2=214089&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/lib/Analysis/ThreadSafety.cpp (original)<br>
+++ cfe/trunk/lib/Analysis/ThreadSafety.cpp Mon Jul 28 10:57:27 2014<br>
@@ -40,682 +40,107 @@<br>
#include "llvm/ADT/StringRef.h"<br>
#include "llvm/Support/raw_ostream.h"<br>
#include <algorithm><br>
+#include <ostream><br>
+#include <sstream><br>
#include <utility><br>
#include <vector><br>
<br>
-using namespace clang;<br>
-using namespace thread_safety;<br>
+<br>
+namespace clang {<br>
+namespace threadSafety {<br>
<br>
// Key method definition<br>
ThreadSafetyHandler::~ThreadSafetyHandler() {}<br>
<br>
-namespace {<br>
-<br>
-/// SExpr implements a simple expression language that is used to store,<br>
-/// compare, and pretty-print C++ expressions. Unlike a clang Expr, a SExpr<br>
-/// does not capture surface syntax, and it does not distinguish between<br>
-/// C++ concepts, like pointers and references, that have no real semantic<br>
-/// differences. This simplicity allows SExprs to be meaningfully compared,<br>
-/// e.g.<br>
-/// (x) = x<br>
-/// (*this).foo = this->foo<br>
-/// *&a = a<br>
-///<br>
-/// Thread-safety analysis works by comparing lock expressions. Within the<br>
-/// body of a function, an expression such as "x->foo-><a href="http://bar.mu" target="_blank">bar.mu</a>" will resolve to<br>
-/// a particular mutex object at run-time. Subsequent occurrences of the same<br>
-/// expression (where "same" means syntactic equality) will refer to the same<br>
-/// run-time object if three conditions hold:<br>
-/// (1) Local variables in the expression, such as "x" have not changed.<br>
-/// (2) Values on the heap that affect the expression have not changed.<br>
-/// (3) The expression involves only pure function calls.<br>
-///<br>
-/// The current implementation assumes, but does not verify, that multiple uses<br>
-/// of the same lock expression satisfies these criteria.<br>
-class SExpr {<br>
-private:<br>
- enum ExprOp {<br>
- EOP_Nop, ///< No-op<br>
- EOP_Wildcard, ///< Matches anything.<br>
- EOP_Universal, ///< Universal lock.<br>
- EOP_This, ///< This keyword.<br>
- EOP_NVar, ///< Named variable.<br>
- EOP_LVar, ///< Local variable.<br>
- EOP_Dot, ///< Field access<br>
- EOP_Call, ///< Function call<br>
- EOP_MCall, ///< Method call<br>
- EOP_Index, ///< Array index<br>
- EOP_Unary, ///< Unary operation<br>
- EOP_Binary, ///< Binary operation<br>
- EOP_Unknown ///< Catchall for everything else<br>
- };<br>
-<br>
-<br>
- class SExprNode {<br>
- private:<br>
- unsigned char Op; ///< Opcode of the root node<br>
- unsigned char Flags; ///< Additional opcode-specific data<br>
- unsigned short Sz; ///< Number of child nodes<br>
- const void* Data; ///< Additional opcode-specific data<br>
-<br>
- public:<br>
- SExprNode(ExprOp O, unsigned F, const void* D)<br>
- : Op(static_cast<unsigned char>(O)),<br>
- Flags(static_cast<unsigned char>(F)), Sz(1), Data(D)<br>
- { }<br>
-<br>
- unsigned size() const { return Sz; }<br>
- void setSize(unsigned S) { Sz = S; }<br>
-<br>
- ExprOp kind() const { return static_cast<ExprOp>(Op); }<br>
-<br>
- const NamedDecl* getNamedDecl() const {<br>
- assert(Op == EOP_NVar || Op == EOP_LVar || Op == EOP_Dot);<br>
- return reinterpret_cast<const NamedDecl*>(Data);<br>
- }<br>
-<br>
- const NamedDecl* getFunctionDecl() const {<br>
- assert(Op == EOP_Call || Op == EOP_MCall);<br>
- return reinterpret_cast<const NamedDecl*>(Data);<br>
- }<br>
-<br>
- bool isArrow() const { return Op == EOP_Dot && Flags == 1; }<br>
- void setArrow(bool A) { Flags = A ? 1 : 0; }<br>
-<br>
- unsigned arity() const {<br>
- switch (Op) {<br>
- case EOP_Nop: return 0;<br>
- case EOP_Wildcard: return 0;<br>
- case EOP_Universal: return 0;<br>
- case EOP_NVar: return 0;<br>
- case EOP_LVar: return 0;<br>
- case EOP_This: return 0;<br>
- case EOP_Dot: return 1;<br>
- case EOP_Call: return Flags+1; // First arg is function.<br>
- case EOP_MCall: return Flags+1; // First arg is implicit obj.<br>
- case EOP_Index: return 2;<br>
- case EOP_Unary: return 1;<br>
- case EOP_Binary: return 2;<br>
- case EOP_Unknown: return Flags;<br>
- }<br>
- return 0;<br>
- }<br>
-<br>
- bool operator==(const SExprNode& Other) const {<br>
- // Ignore flags and size -- they don't matter.<br>
- return (Op == Other.Op &&<br>
- Data == Other.Data);<br>
- }<br>
-<br>
- bool operator!=(const SExprNode& Other) const {<br>
- return !(*this == Other);<br>
- }<br>
-<br>
- bool matches(const SExprNode& Other) const {<br>
- return (*this == Other) ||<br>
- (Op == EOP_Wildcard) ||<br>
- (Other.Op == EOP_Wildcard);<br>
- }<br>
- };<br>
-<br>
-<br>
- /// \brief Encapsulates the lexical context of a function call. The lexical<br>
- /// context includes the arguments to the call, including the implicit object<br>
- /// argument. When an attribute containing a mutex expression is attached to<br>
- /// a method, the expression may refer to formal parameters of the method.<br>
- /// Actual arguments must be substituted for formal parameters to derive<br>
- /// the appropriate mutex expression in the lexical context where the function<br>
- /// is called. PrevCtx holds the context in which the arguments themselves<br>
- /// should be evaluated; multiple calling contexts can be chained together<br>
- /// by the lock_returned attribute.<br>
- struct CallingContext {<br>
- const NamedDecl* AttrDecl; // The decl to which the attribute is attached.<br>
- const Expr* SelfArg; // Implicit object argument -- e.g. 'this'<br>
- bool SelfArrow; // is Self referred to with -> or .?<br>
- unsigned NumArgs; // Number of funArgs<br>
- const Expr* const* FunArgs; // Function arguments<br>
- CallingContext* PrevCtx; // The previous context; or 0 if none.<br>
-<br>
- CallingContext(const NamedDecl *D)<br>
- : AttrDecl(D), SelfArg(nullptr), SelfArrow(false), NumArgs(0),<br>
- FunArgs(nullptr), PrevCtx(nullptr) {}<br>
- };<br>
-<br>
- typedef SmallVector<SExprNode, 4> NodeVector;<br>
-<br>
-private:<br>
- // A SExpr is a list of SExprNodes in prefix order. The Size field allows<br>
- // the list to be traversed as a tree.<br>
- NodeVector NodeVec;<br>
-<br>
-private:<br>
- unsigned make(ExprOp O, unsigned F = 0, const void *D = nullptr) {<br>
- NodeVec.push_back(SExprNode(O, F, D));<br>
- return NodeVec.size() - 1;<br>
- }<br>
-<br>
- unsigned makeNop() {<br>
- return make(EOP_Nop);<br>
- }<br>
-<br>
- unsigned makeWildcard() {<br>
- return make(EOP_Wildcard);<br>
- }<br>
-<br>
- unsigned makeUniversal() {<br>
- return make(EOP_Universal);<br>
- }<br>
+class TILPrinter :<br>
+ public til::PrettyPrinter<TILPrinter, llvm::raw_ostream> {};<br>
<br>
- unsigned makeNamedVar(const NamedDecl *D) {<br>
- return make(EOP_NVar, 0, D);<br>
- }<br>
-<br>
- unsigned makeLocalVar(const NamedDecl *D) {<br>
- return make(EOP_LVar, 0, D);<br>
- }<br>
-<br>
- unsigned makeThis() {<br>
- return make(EOP_This);<br>
- }<br>
<br>
- unsigned makeDot(const NamedDecl *D, bool Arrow) {<br>
- return make(EOP_Dot, Arrow ? 1 : 0, D);<br>
- }<br>
-<br>
- unsigned makeCall(unsigned NumArgs, const NamedDecl *D) {<br>
- return make(EOP_Call, NumArgs, D);<br>
- }<br>
-<br>
- // Grab the very first declaration of virtual method D<br>
- const CXXMethodDecl* getFirstVirtualDecl(const CXXMethodDecl *D) {<br>
- while (true) {<br>
- D = D->getCanonicalDecl();<br>
- CXXMethodDecl::method_iterator I = D->begin_overridden_methods(),<br>
- E = D->end_overridden_methods();<br>
- if (I == E)<br>
- return D; // Method does not override anything<br>
- D = *I; // FIXME: this does not work with multiple inheritance.<br>
- }<br>
- return nullptr;<br>
- }<br>
-<br>
- unsigned makeMCall(unsigned NumArgs, const CXXMethodDecl *D) {<br>
- return make(EOP_MCall, NumArgs, getFirstVirtualDecl(D));<br>
- }<br>
+/// Issue a warning about an invalid lock expression<br>
+static void warnInvalidLock(ThreadSafetyHandler &Handler,<br>
+ const Expr *MutexExp, const NamedDecl *D,<br>
+ const Expr *DeclExp, StringRef Kind) {<br>
+ SourceLocation Loc;<br>
+ if (DeclExp)<br>
+ Loc = DeclExp->getExprLoc();<br>
<br>
- unsigned makeIndex() {<br>
- return make(EOP_Index);<br>
- }<br>
-<br>
- unsigned makeUnary() {<br>
- return make(EOP_Unary);<br>
- }<br>
-<br>
- unsigned makeBinary() {<br>
- return make(EOP_Binary);<br>
- }<br>
-<br>
- unsigned makeUnknown(unsigned Arity) {<br>
- return make(EOP_Unknown, Arity);<br>
- }<br>
-<br>
- inline bool isCalleeArrow(const Expr *E) {<br>
- const MemberExpr *ME = dyn_cast<MemberExpr>(E->IgnoreParenCasts());<br>
- return ME ? ME->isArrow() : false;<br>
- }<br>
-<br>
- /// Build an SExpr from the given C++ expression.<br>
- /// Recursive function that terminates on DeclRefExpr.<br>
- /// Note: this function merely creates a SExpr; it does not check to<br>
- /// ensure that the original expression is a valid mutex expression.<br>
- ///<br>
- /// NDeref returns the number of Derefence and AddressOf operations<br>
- /// preceding the Expr; this is used to decide whether to pretty-print<br>
- /// SExprs with . or ->.<br>
- unsigned buildSExpr(const Expr *Exp, CallingContext *CallCtx,<br>
- int *NDeref = nullptr) {<br>
- if (!Exp)<br>
- return 0;<br>
-<br>
- if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Exp)) {<br>
- const NamedDecl *ND = cast<NamedDecl>(DRE->getDecl()->getCanonicalDecl());<br>
- const ParmVarDecl *PV = dyn_cast_or_null<ParmVarDecl>(ND);<br>
- if (PV) {<br>
- const FunctionDecl *FD =<br>
- cast<FunctionDecl>(PV->getDeclContext())->getCanonicalDecl();<br>
- unsigned i = PV->getFunctionScopeIndex();<br>
-<br>
- if (CallCtx && CallCtx->FunArgs &&<br>
- FD == CallCtx->AttrDecl->getCanonicalDecl()) {<br>
- // Substitute call arguments for references to function parameters<br>
- assert(i < CallCtx->NumArgs);<br>
- return buildSExpr(CallCtx->FunArgs[i], CallCtx->PrevCtx, NDeref);<br>
- }<br>
- // Map the param back to the param of the original function declaration.<br>
- makeNamedVar(FD->getParamDecl(i));<br>
- return 1;<br>
- }<br>
- // Not a function parameter -- just store the reference.<br>
- makeNamedVar(ND);<br>
- return 1;<br>
- } else if (isa<CXXThisExpr>(Exp)) {<br>
- // Substitute parent for 'this'<br>
- if (CallCtx && CallCtx->SelfArg) {<br>
- if (!CallCtx->SelfArrow && NDeref)<br>
- // 'this' is a pointer, but self is not, so need to take address.<br>
- --(*NDeref);<br>
- return buildSExpr(CallCtx->SelfArg, CallCtx->PrevCtx, NDeref);<br>
- }<br>
- else {<br>
- makeThis();<br>
- return 1;<br>
- }<br>
- } else if (const MemberExpr *ME = dyn_cast<MemberExpr>(Exp)) {<br>
- const NamedDecl *ND = ME->getMemberDecl();<br>
- int ImplicitDeref = ME->isArrow() ? 1 : 0;<br>
- unsigned Root = makeDot(ND, false);<br>
- unsigned Sz = buildSExpr(ME->getBase(), CallCtx, &ImplicitDeref);<br>
- NodeVec[Root].setArrow(ImplicitDeref > 0);<br>
- NodeVec[Root].setSize(Sz + 1);<br>
- return Sz + 1;<br>
- } else if (const CXXMemberCallExpr *CMCE = dyn_cast<CXXMemberCallExpr>(Exp)) {<br>
- // When calling a function with a lock_returned attribute, replace<br>
- // the function call with the expression in lock_returned.<br>
- const CXXMethodDecl *MD = CMCE->getMethodDecl()->getMostRecentDecl();<br>
- if (LockReturnedAttr* At = MD->getAttr<LockReturnedAttr>()) {<br>
- CallingContext LRCallCtx(CMCE->getMethodDecl());<br>
- LRCallCtx.SelfArg = CMCE->getImplicitObjectArgument();<br>
- LRCallCtx.SelfArrow = isCalleeArrow(CMCE->getCallee());<br>
- LRCallCtx.NumArgs = CMCE->getNumArgs();<br>
- LRCallCtx.FunArgs = CMCE->getArgs();<br>
- LRCallCtx.PrevCtx = CallCtx;<br>
- return buildSExpr(At->getArg(), &LRCallCtx);<br>
- }<br>
- // Hack to treat smart pointers and iterators as pointers;<br>
- // ignore any method named get().<br>
- if (CMCE->getMethodDecl()->getNameAsString() == "get" &&<br>
- CMCE->getNumArgs() == 0) {<br>
- if (NDeref && isCalleeArrow(CMCE->getCallee()))<br>
- ++(*NDeref);<br>
- return buildSExpr(CMCE->getImplicitObjectArgument(), CallCtx, NDeref);<br>
- }<br>
- unsigned NumCallArgs = CMCE->getNumArgs();<br>
- unsigned Root = makeMCall(NumCallArgs, CMCE->getMethodDecl());<br>
- unsigned Sz = buildSExpr(CMCE->getImplicitObjectArgument(), CallCtx);<br>
- const Expr* const* CallArgs = CMCE->getArgs();<br>
- for (unsigned i = 0; i < NumCallArgs; ++i) {<br>
- Sz += buildSExpr(CallArgs[i], CallCtx);<br>
- }<br>
- NodeVec[Root].setSize(Sz + 1);<br>
- return Sz + 1;<br>
- } else if (const CallExpr *CE = dyn_cast<CallExpr>(Exp)) {<br>
- const FunctionDecl *FD = CE->getDirectCallee()->getMostRecentDecl();<br>
- if (LockReturnedAttr* At = FD->getAttr<LockReturnedAttr>()) {<br>
- CallingContext LRCallCtx(CE->getDirectCallee());<br>
- LRCallCtx.NumArgs = CE->getNumArgs();<br>
- LRCallCtx.FunArgs = CE->getArgs();<br>
- LRCallCtx.PrevCtx = CallCtx;<br>
- return buildSExpr(At->getArg(), &LRCallCtx);<br>
- }<br>
- // Treat smart pointers and iterators as pointers;<br>
- // ignore the * and -> operators.<br>
- if (const CXXOperatorCallExpr *OE = dyn_cast<CXXOperatorCallExpr>(CE)) {<br>
- OverloadedOperatorKind k = OE->getOperator();<br>
- if (k == OO_Star) {<br>
- if (NDeref) ++(*NDeref);<br>
- return buildSExpr(OE->getArg(0), CallCtx, NDeref);<br>
- }<br>
- else if (k == OO_Arrow) {<br>
- return buildSExpr(OE->getArg(0), CallCtx, NDeref);<br>
- }<br>
- }<br>
- unsigned NumCallArgs = CE->getNumArgs();<br>
- unsigned Root = makeCall(NumCallArgs, nullptr);<br>
- unsigned Sz = buildSExpr(CE->getCallee(), CallCtx);<br>
- const Expr* const* CallArgs = CE->getArgs();<br>
- for (unsigned i = 0; i < NumCallArgs; ++i) {<br>
- Sz += buildSExpr(CallArgs[i], CallCtx);<br>
- }<br>
- NodeVec[Root].setSize(Sz+1);<br>
- return Sz+1;<br>
- } else if (const BinaryOperator *BOE = dyn_cast<BinaryOperator>(Exp)) {<br>
- unsigned Root = makeBinary();<br>
- unsigned Sz = buildSExpr(BOE->getLHS(), CallCtx);<br>
- Sz += buildSExpr(BOE->getRHS(), CallCtx);<br>
- NodeVec[Root].setSize(Sz);<br>
- return Sz;<br>
- } else if (const UnaryOperator *UOE = dyn_cast<UnaryOperator>(Exp)) {<br>
- // Ignore & and * operators -- they're no-ops.<br>
- // However, we try to figure out whether the expression is a pointer,<br>
- // so we can use . and -> appropriately in error messages.<br>
- if (UOE->getOpcode() == UO_Deref) {<br>
- if (NDeref) ++(*NDeref);<br>
- return buildSExpr(UOE->getSubExpr(), CallCtx, NDeref);<br>
- }<br>
- if (UOE->getOpcode() == UO_AddrOf) {<br>
- if (DeclRefExpr* DRE = dyn_cast<DeclRefExpr>(UOE->getSubExpr())) {<br>
- if (DRE->getDecl()->isCXXInstanceMember()) {<br>
- // This is a pointer-to-member expression, e.g. &MyClass::mu_.<br>
- // We interpret this syntax specially, as a wildcard.<br>
- unsigned Root = makeDot(DRE->getDecl(), false);<br>
- makeWildcard();<br>
- NodeVec[Root].setSize(2);<br>
- return 2;<br>
- }<br>
- }<br>
- if (NDeref) --(*NDeref);<br>
- return buildSExpr(UOE->getSubExpr(), CallCtx, NDeref);<br>
- }<br>
- unsigned Root = makeUnary();<br>
- unsigned Sz = buildSExpr(UOE->getSubExpr(), CallCtx);<br>
- NodeVec[Root].setSize(Sz);<br>
- return Sz;<br>
- } else if (const ArraySubscriptExpr *ASE =<br>
- dyn_cast<ArraySubscriptExpr>(Exp)) {<br>
- unsigned Root = makeIndex();<br>
- unsigned Sz = buildSExpr(ASE->getBase(), CallCtx);<br>
- Sz += buildSExpr(ASE->getIdx(), CallCtx);<br>
- NodeVec[Root].setSize(Sz);<br>
- return Sz;<br>
- } else if (const AbstractConditionalOperator *CE =<br>
- dyn_cast<AbstractConditionalOperator>(Exp)) {<br>
- unsigned Root = makeUnknown(3);<br>
- unsigned Sz = buildSExpr(CE->getCond(), CallCtx);<br>
- Sz += buildSExpr(CE->getTrueExpr(), CallCtx);<br>
- Sz += buildSExpr(CE->getFalseExpr(), CallCtx);<br>
- NodeVec[Root].setSize(Sz);<br>
- return Sz;<br>
- } else if (const ChooseExpr *CE = dyn_cast<ChooseExpr>(Exp)) {<br>
- unsigned Root = makeUnknown(3);<br>
- unsigned Sz = buildSExpr(CE->getCond(), CallCtx);<br>
- Sz += buildSExpr(CE->getLHS(), CallCtx);<br>
- Sz += buildSExpr(CE->getRHS(), CallCtx);<br>
- NodeVec[Root].setSize(Sz);<br>
- return Sz;<br>
- } else if (const CastExpr *CE = dyn_cast<CastExpr>(Exp)) {<br>
- return buildSExpr(CE->getSubExpr(), CallCtx, NDeref);<br>
- } else if (const ParenExpr *PE = dyn_cast<ParenExpr>(Exp)) {<br>
- return buildSExpr(PE->getSubExpr(), CallCtx, NDeref);<br>
- } else if (const ExprWithCleanups *EWC = dyn_cast<ExprWithCleanups>(Exp)) {<br>
- return buildSExpr(EWC->getSubExpr(), CallCtx, NDeref);<br>
- } else if (const CXXBindTemporaryExpr *E = dyn_cast<CXXBindTemporaryExpr>(Exp)) {<br>
- return buildSExpr(E->getSubExpr(), CallCtx, NDeref);<br>
- } else if (isa<CharacterLiteral>(Exp) ||<br>
- isa<CXXNullPtrLiteralExpr>(Exp) ||<br>
- isa<GNUNullExpr>(Exp) ||<br>
- isa<CXXBoolLiteralExpr>(Exp) ||<br>
- isa<FloatingLiteral>(Exp) ||<br>
- isa<ImaginaryLiteral>(Exp) ||<br>
- isa<IntegerLiteral>(Exp) ||<br>
- isa<StringLiteral>(Exp) ||<br>
- isa<ObjCStringLiteral>(Exp)) {<br>
- makeNop();<br>
- return 1; // FIXME: Ignore literals for now<br>
- } else {<br>
- makeNop();<br>
- return 1; // Ignore. FIXME: mark as invalid expression?<br>
- }<br>
- }<br>
-<br>
- /// \brief Construct a SExpr from an expression.<br>
- /// \param MutexExp The original mutex expression within an attribute<br>
- /// \param DeclExp An expression involving the Decl on which the attribute<br>
- /// occurs.<br>
- /// \param D The declaration to which the lock/unlock attribute is attached.<br>
- void buildSExprFromExpr(const Expr *MutexExp, const Expr *DeclExp,<br>
- const NamedDecl *D, VarDecl *SelfDecl = nullptr) {<br>
- CallingContext CallCtx(D);<br>
-<br>
- if (MutexExp) {<br>
- if (const StringLiteral* SLit = dyn_cast<StringLiteral>(MutexExp)) {<br>
- if (SLit->getString() == StringRef("*"))<br>
- // The "*" expr is a universal lock, which essentially turns off<br>
- // checks until it is removed from the lockset.<br>
- makeUniversal();<br>
- else<br>
- // Ignore other string literals for now.<br>
- makeNop();<br>
- return;<br>
- }<br>
- }<br>
-<br>
- // If we are processing a raw attribute expression, with no substitutions.<br>
- if (!DeclExp) {<br>
- buildSExpr(MutexExp, nullptr);<br>
- return;<br>
- }<br>
-<br>
- // Examine DeclExp to find SelfArg and FunArgs, which are used to substitute<br>
- // for formal parameters when we call buildMutexID later.<br>
- if (const MemberExpr *ME = dyn_cast<MemberExpr>(DeclExp)) {<br>
- CallCtx.SelfArg = ME->getBase();<br>
- CallCtx.SelfArrow = ME->isArrow();<br>
- } else if (const CXXMemberCallExpr *CE =<br>
- dyn_cast<CXXMemberCallExpr>(DeclExp)) {<br>
- CallCtx.SelfArg = CE->getImplicitObjectArgument();<br>
- CallCtx.SelfArrow = isCalleeArrow(CE->getCallee());<br>
- CallCtx.NumArgs = CE->getNumArgs();<br>
- CallCtx.FunArgs = CE->getArgs();<br>
- } else if (const CallExpr *CE = dyn_cast<CallExpr>(DeclExp)) {<br>
- CallCtx.NumArgs = CE->getNumArgs();<br>
- CallCtx.FunArgs = CE->getArgs();<br>
- } else if (const CXXConstructExpr *CE =<br>
- dyn_cast<CXXConstructExpr>(DeclExp)) {<br>
- CallCtx.SelfArg = nullptr; // Will be set below<br>
- CallCtx.NumArgs = CE->getNumArgs();<br>
- CallCtx.FunArgs = CE->getArgs();<br>
- } else if (D && isa<CXXDestructorDecl>(D)) {<br>
- // There's no such thing as a "destructor call" in the AST.<br>
- CallCtx.SelfArg = DeclExp;<br>
- }<br>
-<br>
- // Hack to handle constructors, where self cannot be recovered from<br>
- // the expression.<br>
- if (SelfDecl && !CallCtx.SelfArg) {<br>
- DeclRefExpr SelfDRE(SelfDecl, false, SelfDecl->getType(), VK_LValue,<br>
- SelfDecl->getLocation());<br>
- CallCtx.SelfArg = &SelfDRE;<br>
-<br>
- // If the attribute has no arguments, then assume the argument is "this".<br>
- if (!MutexExp)<br>
- buildSExpr(CallCtx.SelfArg, nullptr);<br>
- else // For most attributes.<br>
- buildSExpr(MutexExp, &CallCtx);<br>
- return;<br>
- }<br>
+ // FIXME: add a note about the attribute location in MutexExp or D<br>
+ if (Loc.isValid())<br>
+ Handler.handleInvalidLockExp(Kind, Loc);<br>
+}<br>
<br>
- // If the attribute has no arguments, then assume the argument is "this".<br>
- if (!MutexExp)<br>
- buildSExpr(CallCtx.SelfArg, nullptr);<br>
- else // For most attributes.<br>
- buildSExpr(MutexExp, &CallCtx);<br>
- }<br>
<br>
- /// \brief Get index of next sibling of node i.<br>
- unsigned getNextSibling(unsigned i) const {<br>
- return i + NodeVec[i].size();<br>
- }<br>
+// Various helper functions on til::SExpr<br>
+namespace sx {<br>
<br>
-public:<br>
- explicit SExpr(clang::Decl::EmptyShell e) { NodeVec.clear(); }<br>
-<br>
- /// \param MutexExp The original mutex expression within an attribute<br>
- /// \param DeclExp An expression involving the Decl on which the attribute<br>
- /// occurs.<br>
- /// \param D The declaration to which the lock/unlock attribute is attached.<br>
- /// Caller must check isValid() after construction.<br>
- SExpr(const Expr *MutexExp, const Expr *DeclExp, const NamedDecl *D,<br>
- VarDecl *SelfDecl = nullptr) {<br>
- buildSExprFromExpr(MutexExp, DeclExp, D, SelfDecl);<br>
- }<br>
+bool isUniversal(const til::SExpr *E) {<br>
+ return isa<til::Wildcard>(E);<br>
+}<br>
<br>
- /// Return true if this is a valid decl sequence.<br>
- /// Caller must call this by hand after construction to handle errors.<br>
- bool isValid() const {<br>
- return !NodeVec.empty();<br>
- }<br>
+bool equals(const til::SExpr *E1, const til::SExpr *E2) {<br>
+ return til::EqualsComparator::compareExprs(E1, E2);<br>
+}<br>
<br>
- bool shouldIgnore() const {<br>
- // Nop is a mutex that we have decided to deliberately ignore.<br>
- assert(NodeVec.size() > 0 && "Invalid Mutex");<br>
- return NodeVec[0].kind() == EOP_Nop;<br>
+const til::SExpr* ignorePtrCasts(const til::SExpr *E) {<br>
+ if (auto *CE = dyn_cast<til::Cast>(E)) {<br>
+ if (CE->castOpcode() == til::CAST_objToPtr)<br>
+ return CE->expr();<br>
}<br>
+ return E;<br>
+}<br>
<br>
- bool isUniversal() const {<br>
- assert(NodeVec.size() > 0 && "Invalid Mutex");<br>
- return NodeVec[0].kind() == EOP_Universal;<br>
- }<br>
+bool matches(const til::SExpr *E1, const til::SExpr *E2) {<br>
+ // We treat a top-level wildcard as the "univsersal" lock.<br>
+ // It matches everything for the purpose of checking locks, but not<br>
+ // for unlocking them.<br>
+ if (isa<til::Wildcard>(E1))<br>
+ return isa<til::Wildcard>(E2);<br>
+ if (isa<til::Wildcard>(E2))<br>
+ return isa<til::Wildcard>(E1);<br>
<br>
- /// Issue a warning about an invalid lock expression<br>
- static void warnInvalidLock(ThreadSafetyHandler &Handler,<br>
- const Expr *MutexExp, const Expr *DeclExp,<br>
- const NamedDecl *D, StringRef Kind) {<br>
- SourceLocation Loc;<br>
- if (DeclExp)<br>
- Loc = DeclExp->getExprLoc();<br>
+ return til::MatchComparator::compareExprs(E1, E2);<br>
+}<br>
<br>
- // FIXME: add a note about the attribute location in MutexExp or D<br>
- if (Loc.isValid())<br>
- Handler.handleInvalidLockExp(Kind, Loc);<br>
- }<br>
+bool partiallyMatches(const til::SExpr *E1, const til::SExpr *E2) {<br>
+ auto *PE1 = dyn_cast_or_null<til::Project>(E1);<br>
+ if (!PE1)<br>
+ return false;<br>
+ auto *PE2 = dyn_cast_or_null<til::Project>(E2);<br>
+ if (!PE2)<br>
+ return false;<br>
+ return PE1->clangDecl() == PE2->clangDecl();<br>
+}<br>
<br>
- bool operator==(const SExpr &other) const {<br>
- return NodeVec == other.NodeVec;<br>
- }<br>
+std::string toString(const til::SExpr *E) {<br>
+ std::stringstream ss;<br>
+ til::StdPrinter::print(E, ss);<br>
+ return ss.str();<br>
+}<br>
<br>
- bool operator!=(const SExpr &other) const {<br>
- return !(*this == other);<br>
- }<br>
+bool shouldIgnore(const til::SExpr *E) {<br>
+ if (!E)<br>
+ return true;<br>
+ // Trap mutex expressions like nullptr, or 0.<br>
+ // Any literal value is nonsense.<br>
+ if (isa<til::Literal>(E))<br>
+ return true;<br>
+ return false;<br>
+}<br>
<br>
- bool matches(const SExpr &Other, unsigned i = 0, unsigned j = 0) const {<br>
- if (NodeVec[i].matches(Other.NodeVec[j])) {<br>
- unsigned ni = NodeVec[i].arity();<br>
- unsigned nj = Other.NodeVec[j].arity();<br>
- unsigned n = (ni < nj) ? ni : nj;<br>
- bool Result = true;<br>
- unsigned ci = i+1; // first child of i<br>
- unsigned cj = j+1; // first child of j<br>
- for (unsigned k = 0; k < n;<br>
- ++k, ci=getNextSibling(ci), cj = Other.getNextSibling(cj)) {<br>
- Result = Result && matches(Other, ci, cj);<br>
- }<br>
- return Result;<br>
- }<br>
- return false;<br>
- }<br>
+} // end namespace sx<br>
<br>
- // A partial match between <a href="http://a.mu" target="_blank">a.mu</a> and <a href="http://b.mu" target="_blank">b.mu</a> returns true a and b have the same<br>
- // type (and thus mu refers to the same mutex declaration), regardless of<br>
- // whether a and b are different objects or not.<br>
- bool partiallyMatches(const SExpr &Other) const {<br>
- if (NodeVec[0].kind() == EOP_Dot)<br>
- return NodeVec[0].matches(Other.NodeVec[0]);<br>
- return false;<br>
- }<br>
<br>
- /// \brief Pretty print a lock expression for use in error messages.<br>
- std::string toString(unsigned i = 0) const {<br>
- assert(isValid());<br>
- if (i >= NodeVec.size())<br>
- return "";<br>
-<br>
- const SExprNode* N = &NodeVec[i];<br>
- switch (N->kind()) {<br>
- case EOP_Nop:<br>
- return "_";<br>
- case EOP_Wildcard:<br>
- return "(?)";<br>
- case EOP_Universal:<br>
- return "*";<br>
- case EOP_This:<br>
- return "this";<br>
- case EOP_NVar:<br>
- case EOP_LVar: {<br>
- return N->getNamedDecl()->getNameAsString();<br>
- }<br>
- case EOP_Dot: {<br>
- if (NodeVec[i+1].kind() == EOP_Wildcard) {<br>
- std::string S = "&";<br>
- S += N->getNamedDecl()->getQualifiedNameAsString();<br>
- return S;<br>
- }<br>
- std::string FieldName = N->getNamedDecl()->getNameAsString();<br>
- if (NodeVec[i+1].kind() == EOP_This)<br>
- return FieldName;<br>
-<br>
- std::string S = toString(i+1);<br>
- if (N->isArrow())<br>
- return S + "->" + FieldName;<br>
- else<br>
- return S + "." + FieldName;<br>
- }<br>
- case EOP_Call: {<br>
- std::string S = toString(i+1) + "(";<br>
- unsigned NumArgs = N->arity()-1;<br>
- unsigned ci = getNextSibling(i+1);<br>
- for (unsigned k=0; k<NumArgs; ++k, ci = getNextSibling(ci)) {<br>
- S += toString(ci);<br>
- if (k+1 < NumArgs) S += ",";<br>
- }<br>
- S += ")";<br>
- return S;<br>
- }<br>
- case EOP_MCall: {<br>
- std::string S = "";<br>
- if (NodeVec[i+1].kind() != EOP_This)<br>
- S = toString(i+1) + ".";<br>
- if (const NamedDecl *D = N->getFunctionDecl())<br>
- S += D->getNameAsString() + "(";<br>
- else<br>
- S += "#(";<br>
- unsigned NumArgs = N->arity()-1;<br>
- unsigned ci = getNextSibling(i+1);<br>
- for (unsigned k=0; k<NumArgs; ++k, ci = getNextSibling(ci)) {<br>
- S += toString(ci);<br>
- if (k+1 < NumArgs) S += ",";<br>
- }<br>
- S += ")";<br>
- return S;<br>
- }<br>
- case EOP_Index: {<br>
- std::string S1 = toString(i+1);<br>
- std::string S2 = toString(i+1 + NodeVec[i+1].size());<br>
- return S1 + "[" + S2 + "]";<br>
- }<br>
- case EOP_Unary: {<br>
- std::string S = toString(i+1);<br>
- return "#" + S;<br>
- }<br>
- case EOP_Binary: {<br>
- std::string S1 = toString(i+1);<br>
- std::string S2 = toString(i+1 + NodeVec[i+1].size());<br>
- return "(" + S1 + "#" + S2 + ")";<br>
- }<br>
- case EOP_Unknown: {<br>
- unsigned NumChildren = N->arity();<br>
- if (NumChildren == 0)<br>
- return "(...)";<br>
- std::string S = "(";<br>
- unsigned ci = i+1;<br>
- for (unsigned j = 0; j < NumChildren; ++j, ci = getNextSibling(ci)) {<br>
- S += toString(ci);<br>
- if (j+1 < NumChildren) S += "#";<br>
- }<br>
- S += ")";<br>
- return S;<br>
- }<br>
- }<br>
- return "";<br>
- }<br>
-};<br>
<br>
/// \brief A short list of SExprs<br>
-class MutexIDList : public SmallVector<SExpr, 3> {<br>
+class MutexIDList : public SmallVector<const til::SExpr*, 3> {<br>
public:<br>
/// \brief Push M onto list, but discard duplicates.<br>
- void push_back_nodup(const SExpr& M) {<br>
- if (end() == std::find(begin(), end(), M))<br>
- push_back(M);<br>
+ void push_back_nodup(const til::SExpr *E) {<br>
+ iterator It = std::find_if(begin(), end(), [=](const til::SExpr *E2) {<br>
+ return sx::equals(E, E2);<br>
+ });<br>
+ if (It == end())<br>
+ push_back(E);<br>
}<br>
};<br>
<br>
@@ -735,15 +160,15 @@ struct LockData {<br>
LockKind LKind;<br>
bool Asserted; // for asserted locks<br>
bool Managed; // for ScopedLockable objects<br>
- SExpr UnderlyingMutex; // for ScopedLockable objects<br>
+ const til::SExpr* UnderlyingMutex; // for ScopedLockable objects<br>
<br>
LockData(SourceLocation AcquireLoc, LockKind LKind, bool M=false,<br>
bool Asrt=false)<br>
: AcquireLoc(AcquireLoc), LKind(LKind), Asserted(Asrt), Managed(M),<br>
- UnderlyingMutex(Decl::EmptyShell())<br>
+ UnderlyingMutex(nullptr)<br>
{}<br>
<br>
- LockData(SourceLocation AcquireLoc, LockKind LKind, const SExpr &Mu)<br>
+ LockData(SourceLocation AcquireLoc, LockKind LKind, const til::SExpr *Mu)<br>
: AcquireLoc(AcquireLoc), LKind(LKind), Asserted(false), Managed(false),<br>
UnderlyingMutex(Mu)<br>
{}<br>
@@ -771,10 +196,10 @@ struct LockData {<br>
/// in the program execution. Currently, this is information regarding a lock<br>
/// that is held at that point.<br>
struct FactEntry {<br>
- SExpr MutID;<br>
+ const til::SExpr *MutID;<br>
LockData LDat;<br>
<br>
- FactEntry(const SExpr& M, const LockData& L)<br>
+ FactEntry(const til::SExpr* M, const LockData& L)<br>
: MutID(M), LDat(L)<br>
{ }<br>
};<br>
@@ -789,8 +214,8 @@ private:<br>
std::vector<FactEntry> Facts;<br>
<br>
public:<br>
- FactID newLock(const SExpr& M, const LockData& L) {<br>
- Facts.push_back(FactEntry(M,L));<br>
+ FactID newLock(const til::SExpr *M, const LockData& L) {<br>
+ Facts.push_back(FactEntry(M, L));<br>
return static_cast<unsigned short>(Facts.size() - 1);<br>
}<br>
<br>
@@ -824,66 +249,67 @@ public:<br>
<br>
bool isEmpty() const { return FactIDs.size() == 0; }<br>
<br>
- FactID addLock(FactManager& FM, const SExpr& M, const LockData& L) {<br>
+ FactID addLock(FactManager& FM, const til::SExpr *M, const LockData& L) {<br>
FactID F = FM.newLock(M, L);<br>
FactIDs.push_back(F);<br>
return F;<br>
}<br>
<br>
- bool removeLock(FactManager& FM, const SExpr& M) {<br>
+ bool removeLock(FactManager& FM, const til::SExpr *M) {<br>
unsigned n = FactIDs.size();<br>
if (n == 0)<br>
return false;<br>
<br>
for (unsigned i = 0; i < n-1; ++i) {<br>
- if (FM[FactIDs[i]].MutID.matches(M)) {<br>
+ if (sx::matches(FM[FactIDs[i]].MutID, M)) {<br>
FactIDs[i] = FactIDs[n-1];<br>
FactIDs.pop_back();<br>
return true;<br>
}<br>
}<br>
- if (FM[FactIDs[n-1]].MutID.matches(M)) {<br>
+ if (sx::matches(FM[FactIDs[n-1]].MutID, M)) {<br>
FactIDs.pop_back();<br>
return true;<br>
}<br>
return false;<br>
}<br>
<br>
- iterator findLockIter(FactManager &FM, const SExpr &M) {<br>
+ iterator findLockIter(FactManager &FM, const til::SExpr *M) {<br>
return std::find_if(begin(), end(), [&](FactID ID) {<br>
- return FM[ID].MutID.matches(M);<br>
+ return sx::matches(FM[ID].MutID, M);<br>
});<br>
}<br>
<br>
- LockData *findLock(FactManager &FM, const SExpr &M) const {<br>
+ LockData *findLock(FactManager &FM, const til::SExpr *M) const {<br>
auto I = std::find_if(begin(), end(), [&](FactID ID) {<br>
- return FM[ID].MutID.matches(M);<br>
+ return sx::matches(FM[ID].MutID, M);<br>
});<br>
<br>
return I != end() ? &FM[*I].LDat : nullptr;<br>
}<br>
<br>
- LockData *findLockUniv(FactManager &FM, const SExpr &M) const {<br>
+ LockData *findLockUniv(FactManager &FM, const til::SExpr *M) const {<br>
auto I = std::find_if(begin(), end(), [&](FactID ID) -> bool {<br>
- const SExpr &Expr = FM[ID].MutID;<br>
- return Expr.isUniversal() || Expr.matches(M);<br>
+ const til::SExpr *E = FM[ID].MutID;<br>
+ return sx::isUniversal(E) || sx::matches(E, M);<br>
});<br>
<br>
return I != end() ? &FM[*I].LDat : nullptr;<br>
}<br>
<br>
- FactEntry *findPartialMatch(FactManager &FM, const SExpr &M) const {<br>
+ FactEntry *findPartialMatch(FactManager &FM, const til::SExpr *M) const {<br>
auto I = std::find_if(begin(), end(), [&](FactID ID) {<br>
- return FM[ID].MutID.partiallyMatches(M);<br>
+ return sx::partiallyMatches(FM[ID].MutID, M);<br>
});<br>
<br>
return I != end() ? &FM[*I] : nullptr;<br>
}<br>
};<br>
<br>
+<br>
/// A Lockset maps each SExpr (defined above) to information about how it has<br>
/// been locked.<br>
-typedef llvm::ImmutableMap<SExpr, LockData> Lockset;<br>
+typedef llvm::ImmutableMap<til::SExpr*, LockData> Lockset;<br>
typedef llvm::ImmutableMap<const NamedDecl*, unsigned> LocalVarContext;<br>
<br>
class LocalVariableMap;<br>
@@ -1408,18 +834,24 @@ static void findBlockLocations(CFG *CFGr<br>
class ThreadSafetyAnalyzer {<br>
friend class BuildLockset;<br>
<br>
+ llvm::BumpPtrAllocator Bpa;<br>
+ threadSafety::til::MemRegionRef Arena;<br>
+ threadSafety::SExprBuilder SxBuilder;<br>
+<br>
ThreadSafetyHandler &Handler;<br>
LocalVariableMap LocalVarMap;<br>
FactManager FactMan;<br>
std::vector<CFGBlockInfo> BlockInfo;<br>
<br>
public:<br>
- ThreadSafetyAnalyzer(ThreadSafetyHandler &H) : Handler(H) {}<br>
+ ThreadSafetyAnalyzer(ThreadSafetyHandler &H)<br>
+ : Arena(&Bpa), SxBuilder(Arena), Handler(H) {}<br>
<br>
- void addLock(FactSet &FSet, const SExpr &Mutex, const LockData &LDat,<br>
+ void addLock(FactSet &FSet, const til::SExpr *Mutex, const LockData &LDat,<br>
StringRef DiagKind);<br>
- void removeLock(FactSet &FSet, const SExpr &Mutex, SourceLocation UnlockLoc,<br>
- bool FullyRemove, LockKind Kind, StringRef DiagKind);<br>
+ void removeLock(FactSet &FSet, const til::SExpr *Mutex,<br>
+ SourceLocation UnlockLoc, bool FullyRemove, LockKind Kind,<br>
+ StringRef DiagKind);<br>
<br>
template <typename AttrType><br>
void getMutexIDs(MutexIDList &Mtxs, AttrType *Attr, Expr *Exp,<br>
@@ -1533,16 +965,16 @@ ClassifyDiagnostic(const AttrTy *A) {<br>
/// \brief Add a new lock to the lockset, warning if the lock is already there.<br>
/// \param Mutex -- the Mutex expression for the lock<br>
/// \param LDat -- the LockData for the lock<br>
-void ThreadSafetyAnalyzer::addLock(FactSet &FSet, const SExpr &Mutex,<br>
+void ThreadSafetyAnalyzer::addLock(FactSet &FSet, const til::SExpr *Mutex,<br>
const LockData &LDat, StringRef DiagKind) {<br>
// FIXME: deal with acquired before/after annotations.<br>
// FIXME: Don't always warn when we have support for reentrant locks.<br>
- if (Mutex.shouldIgnore())<br>
+ if (sx::shouldIgnore(Mutex))<br>
return;<br>
<br>
if (FSet.findLock(FactMan, Mutex)) {<br>
if (!LDat.Asserted)<br>
- Handler.handleDoubleLock(DiagKind, Mutex.toString(), LDat.AcquireLoc);<br>
+ Handler.handleDoubleLock(DiagKind, sx::toString(Mutex), LDat.AcquireLoc);<br>
} else {<br>
FSet.addLock(FactMan, Mutex, LDat);<br>
}<br>
@@ -1552,28 +984,28 @@ void ThreadSafetyAnalyzer::addLock(FactS<br>
/// \brief Remove a lock from the lockset, warning if the lock is not there.<br>
/// \param Mutex The lock expression corresponding to the lock to be removed<br>
/// \param UnlockLoc The source location of the unlock (only used in error msg)<br>
-void ThreadSafetyAnalyzer::removeLock(FactSet &FSet, const SExpr &Mutex,<br>
+void ThreadSafetyAnalyzer::removeLock(FactSet &FSet, const til::SExpr *Mutex,<br>
SourceLocation UnlockLoc,<br>
bool FullyRemove, LockKind ReceivedKind,<br>
StringRef DiagKind) {<br>
- if (Mutex.shouldIgnore())<br>
+ if (sx::shouldIgnore(Mutex))<br>
return;<br>
<br>
const LockData *LDat = FSet.findLock(FactMan, Mutex);<br>
if (!LDat) {<br>
- Handler.handleUnmatchedUnlock(DiagKind, Mutex.toString(), UnlockLoc);<br>
+ Handler.handleUnmatchedUnlock(DiagKind, sx::toString(Mutex), UnlockLoc);<br>
return;<br>
}<br>
<br>
// Generic lock removal doesn't care about lock kind mismatches, but<br>
// otherwise diagnose when the lock kinds are mismatched.<br>
if (ReceivedKind != LK_Generic && LDat->LKind != ReceivedKind) {<br>
- Handler.handleIncorrectUnlockKind(DiagKind, Mutex.toString(), LDat->LKind,<br>
- ReceivedKind, UnlockLoc);<br>
+ Handler.handleIncorrectUnlockKind(DiagKind, sx::toString(Mutex),<br>
+ LDat->LKind, ReceivedKind, UnlockLoc);<br>
return;<br>
}<br>
<br>
- if (LDat->UnderlyingMutex.isValid()) {<br>
+ if (LDat->UnderlyingMutex) {<br>
// This is scoped lockable object, which manages the real mutex.<br>
if (FullyRemove) {<br>
// We're destroying the managing object.<br>
@@ -1585,7 +1017,7 @@ void ThreadSafetyAnalyzer::removeLock(Fa<br>
// managing object. Warn on dual release.<br>
if (!FSet.findLock(FactMan, LDat->UnderlyingMutex)) {<br>
Handler.handleUnmatchedUnlock(<br>
- DiagKind, LDat->UnderlyingMutex.toString(), UnlockLoc);<br>
+ DiagKind, sx::toString(LDat->UnderlyingMutex), UnlockLoc);<br>
}<br>
FSet.removeLock(FactMan, LDat->UnderlyingMutex);<br>
return;<br>
@@ -1603,20 +1035,25 @@ void ThreadSafetyAnalyzer::getMutexIDs(M<br>
VarDecl *SelfDecl) {<br>
if (Attr->args_size() == 0) {<br>
// The mutex held is the "this" object.<br>
- SExpr Mu(nullptr, Exp, D, SelfDecl);<br>
- if (!Mu.isValid())<br>
- SExpr::warnInvalidLock(Handler, nullptr, Exp, D,<br>
- ClassifyDiagnostic(Attr));<br>
- else<br>
+ til::SExpr *Mu = SxBuilder.translateAttrExpr(nullptr, D, Exp, SelfDecl);<br>
+ if (Mu && isa<til::Undefined>(Mu)) {<br>
+ warnInvalidLock(Handler, nullptr, D, Exp, ClassifyDiagnostic(Attr));<br>
+ return;<br>
+ }<br>
+ //else<br>
+ if (Mu)<br>
Mtxs.push_back_nodup(Mu);<br>
return;<br>
}<br>
<br>
for (const auto *Arg : Attr->args()) {<br>
- SExpr Mu(Arg, Exp, D, SelfDecl);<br>
- if (!Mu.isValid())<br>
- SExpr::warnInvalidLock(Handler, Arg, Exp, D, ClassifyDiagnostic(Attr));<br>
- else<br>
+ til::SExpr *Mu = SxBuilder.translateAttrExpr(Arg, D, Exp, SelfDecl);<br>
+ if (Mu && isa<til::Undefined>(Mu)) {<br>
+ warnInvalidLock(Handler, nullptr, D, Exp, ClassifyDiagnostic(Attr));<br>
+ return;<br>
+ }<br>
+ //else<br>
+ if (Mu)<br>
Mtxs.push_back_nodup(Mu);<br>
}<br>
}<br>
@@ -1845,11 +1282,12 @@ void BuildLockset::warnIfMutexNotHeld(co<br>
StringRef DiagKind) {<br>
LockKind LK = getLockKindFromAccessKind(AK);<br>
<br>
- SExpr Mutex(MutexExp, Exp, D);<br>
- if (!Mutex.isValid()) {<br>
- SExpr::warnInvalidLock(Analyzer->Handler, MutexExp, Exp, D, DiagKind);<br>
+ til::SExpr *Mutex = Analyzer->SxBuilder.translateAttrExpr(MutexExp, D, Exp);<br>
+ if (!Mutex) {<br>
+ // TODO: invalid locks?<br>
+ // warnInvalidLock(Analyzer->Handler, MutexExp, D, Exp, DiagKind);<br>
return;<br>
- } else if (Mutex.shouldIgnore()) {<br>
+ } else if (sx::shouldIgnore(Mutex)) {<br>
return;<br>
}<br>
<br>
@@ -1861,38 +1299,42 @@ void BuildLockset::warnIfMutexNotHeld(co<br>
if (FEntry) {<br>
// Warn that there's no precise match.<br>
LDat = &FEntry->LDat;<br>
- std::string PartMatchStr = FEntry->MutID.toString();<br>
+ std::string PartMatchStr = sx::toString(FEntry->MutID);<br>
StringRef PartMatchName(PartMatchStr);<br>
- Analyzer->Handler.handleMutexNotHeld(DiagKind, D, POK, Mutex.toString(),<br>
+ Analyzer->Handler.handleMutexNotHeld(DiagKind, D, POK,<br>
+ sx::toString(Mutex),<br>
LK, Exp->getExprLoc(),<br>
&PartMatchName);<br>
} else {<br>
// Warn that there's no match at all.<br>
- Analyzer->Handler.handleMutexNotHeld(DiagKind, D, POK, Mutex.toString(),<br>
+ Analyzer->Handler.handleMutexNotHeld(DiagKind, D, POK,<br>
+ sx::toString(Mutex),<br>
LK, Exp->getExprLoc());<br>
}<br>
NoError = false;<br>
}<br>
// Make sure the mutex we found is the right kind.<br>
if (NoError && LDat && !LDat->isAtLeast(LK))<br>
- Analyzer->Handler.handleMutexNotHeld(DiagKind, D, POK, Mutex.toString(), LK,<br>
- Exp->getExprLoc());<br>
+ Analyzer->Handler.handleMutexNotHeld(DiagKind, D, POK,<br>
+ sx::toString(Mutex),<br>
+ LK, Exp->getExprLoc());<br>
}<br>
<br>
/// \brief Warn if the LSet contains the given lock.<br>
void BuildLockset::warnIfMutexHeld(const NamedDecl *D, const Expr *Exp,<br>
Expr *MutexExp,<br>
StringRef DiagKind) {<br>
- SExpr Mutex(MutexExp, Exp, D);<br>
- if (!Mutex.isValid()) {<br>
- SExpr::warnInvalidLock(Analyzer->Handler, MutexExp, Exp, D, DiagKind);<br>
+ til::SExpr *Mutex = Analyzer->SxBuilder.translateAttrExpr(MutexExp, D, Exp);<br>
+ if (!Mutex) {<br>
+ // TODO: invalid locks?<br>
+ // warnInvalidLock(Analyzer->Handler, MutexExp, D, Exp, DiagKind);<br>
return;<br>
}<br>
<br>
LockData* LDat = FSet.findLock(Analyzer->FactMan, Mutex);<br>
if (LDat)<br>
Analyzer->Handler.handleFunExcludesLock(<br>
- DiagKind, D->getNameAsString(), Mutex.toString(), Exp->getExprLoc());<br>
+ DiagKind, D->getNameAsString(), sx::toString(Mutex), Exp->getExprLoc());<br>
}<br>
<br>
/// \brief Checks guarded_by and pt_guarded_by attributes.<br>
@@ -2085,7 +1527,7 @@ void BuildLockset::handleCall(Expr *Exp,<br>
if (isScopedVar) {<br>
SourceLocation MLoc = VD->getLocation();<br>
DeclRefExpr DRE(VD, false, VD->getType(), VK_LValue, VD->getLocation());<br>
- SExpr SMutex(&DRE, nullptr, nullptr);<br>
+ til::SExpr *SMutex = Analyzer->SxBuilder.translateAttrExpr(&DRE, nullptr);<br>
<br>
for (const auto &M : ExclusiveLocksToAdd)<br>
Analyzer->addLock(FSet, SMutex, LockData(MLoc, LK_Exclusive, M),<br>
@@ -2093,6 +1535,12 @@ void BuildLockset::handleCall(Expr *Exp,<br>
for (const auto &M : SharedLocksToAdd)<br>
Analyzer->addLock(FSet, SMutex, LockData(MLoc, LK_Shared, M),<br>
CapDiagKind);<br>
+<br>
+ // handle corner case where the underlying mutex is invalid<br>
+ if (ExclusiveLocksToAdd.size() == 0 && SharedLocksToAdd.size() == 0) {<br>
+ Analyzer->addLock(FSet, SMutex, LockData(MLoc, LK_Exclusive),<br>
+ CapDiagKind);<br>
+ }<br>
}<br>
<br>
// Remove locks.<br>
@@ -2254,14 +1702,14 @@ void ThreadSafetyAnalyzer::intersectAndW<br>
<br>
// Find locks in FSet2 that conflict or are not in FSet1, and warn.<br>
for (const auto &Fact : FSet2) {<br>
- const SExpr &FSet2Mutex = FactMan[Fact].MutID;<br>
+ const til::SExpr *FSet2Mutex = FactMan[Fact].MutID;<br>
const LockData &LDat2 = FactMan[Fact].LDat;<br>
FactSet::iterator I1 = FSet1.findLockIter(FactMan, FSet2Mutex);<br>
<br>
if (I1 != FSet1.end()) {<br>
const LockData* LDat1 = &FactMan[*I1].LDat;<br>
if (LDat1->LKind != LDat2.LKind) {<br>
- Handler.handleExclusiveAndShared("mutex", FSet2Mutex.toString(),<br>
+ Handler.handleExclusiveAndShared("mutex", sx::toString(FSet2Mutex),<br>
LDat2.AcquireLoc, LDat1->AcquireLoc);<br>
if (Modify && LDat1->LKind != LK_Exclusive) {<br>
// Take the exclusive lock, which is the one in FSet2.<br>
@@ -2273,40 +1721,42 @@ void ThreadSafetyAnalyzer::intersectAndW<br>
*I1 = Fact;<br>
}<br>
} else {<br>
- if (LDat2.UnderlyingMutex.isValid()) {<br>
+ if (LDat2.UnderlyingMutex) {<br>
if (FSet2.findLock(FactMan, LDat2.UnderlyingMutex)) {<br>
// If this is a scoped lock that manages another mutex, and if the<br>
// underlying mutex is still held, then warn about the underlying<br>
// mutex.<br>
Handler.handleMutexHeldEndOfScope("mutex",<br>
- LDat2.UnderlyingMutex.toString(),<br>
+ sx::toString(LDat2.UnderlyingMutex),<br>
LDat2.AcquireLoc, JoinLoc, LEK1);<br>
}<br>
}<br>
- else if (!LDat2.Managed && !FSet2Mutex.isUniversal() && !LDat2.Asserted)<br>
- Handler.handleMutexHeldEndOfScope("mutex", FSet2Mutex.toString(),<br>
+ else if (!LDat2.Managed && !sx::isUniversal(FSet2Mutex) &&<br>
+ !LDat2.Asserted)<br>
+ Handler.handleMutexHeldEndOfScope("mutex", sx::toString(FSet2Mutex),<br>
LDat2.AcquireLoc, JoinLoc, LEK1);<br>
}<br>
}<br>
<br>
// Find locks in FSet1 that are not in FSet2, and remove them.<br>
for (const auto &Fact : FSet1Orig) {<br>
- const SExpr &FSet1Mutex = FactMan[Fact].MutID;<br>
+ const til::SExpr *FSet1Mutex = FactMan[Fact].MutID;<br>
const LockData &LDat1 = FactMan[Fact].LDat;<br>
<br>
if (!FSet2.findLock(FactMan, FSet1Mutex)) {<br>
- if (LDat1.UnderlyingMutex.isValid()) {<br>
+ if (LDat1.UnderlyingMutex) {<br>
if (FSet1Orig.findLock(FactMan, LDat1.UnderlyingMutex)) {<br>
// If this is a scoped lock that manages another mutex, and if the<br>
// underlying mutex is still held, then warn about the underlying<br>
// mutex.<br>
Handler.handleMutexHeldEndOfScope("mutex",<br>
- LDat1.UnderlyingMutex.toString(),<br>
+ sx::toString(LDat1.UnderlyingMutex),<br>
LDat1.AcquireLoc, JoinLoc, LEK1);<br>
}<br>
}<br>
- else if (!LDat1.Managed && !FSet1Mutex.isUniversal() && !LDat1.Asserted)<br>
- Handler.handleMutexHeldEndOfScope("mutex", FSet1Mutex.toString(),<br>
+ else if (!LDat1.Managed && !sx::isUniversal(FSet1Mutex) &&<br>
+ !LDat1.Asserted)<br>
+ Handler.handleMutexHeldEndOfScope("mutex", sx::toString(FSet1Mutex),<br>
LDat1.AcquireLoc, JoinLoc, LEK2);<br>
if (Modify)<br>
FSet1.removeLock(FactMan, FSet1Mutex);<br>
@@ -2618,11 +2068,6 @@ void ThreadSafetyAnalyzer::runAnalysis(A<br>
false);<br>
}<br>
<br>
-} // end anonymous namespace<br>
-<br>
-<br>
-namespace clang {<br>
-namespace thread_safety {<br>
<br>
/// \brief Check a function's CFG for thread-safety violations.<br>
///<br>
@@ -2647,4 +2092,4 @@ LockKind getLockKindFromAccessKind(Acces<br>
llvm_unreachable("Unknown AccessKind");<br>
}<br>
<br>
-}} // end namespace clang::thread_safety<br>
+}} // end namespace clang::threadSafety<br>
<br>
Modified: cfe/trunk/lib/Analysis/ThreadSafetyCommon.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/ThreadSafetyCommon.cpp?rev=214089&r1=214088&r2=214089&view=diff" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/ThreadSafetyCommon.cpp?rev=214089&r1=214088&r2=214089&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/lib/Analysis/ThreadSafetyCommon.cpp (original)<br>
+++ cfe/trunk/lib/Analysis/ThreadSafetyCommon.cpp Mon Jul 28 10:57:27 2014<br>
@@ -91,6 +91,102 @@ til::SCFG *SExprBuilder::buildCFG(CFGWal<br>
}<br>
<br>
<br>
+<br>
+inline bool isCalleeArrow(const Expr *E) {<br>
+ const MemberExpr *ME = dyn_cast<MemberExpr>(E->IgnoreParenCasts());<br>
+ return ME ? ME->isArrow() : false;<br>
+}<br>
+<br>
+<br>
+/// \brief Translate a clang expression in an attribute to a til::SExpr.<br>
+/// Constructs the context from D, DeclExp, and SelfDecl.<br>
+///<br>
+/// \param AttrExp The expression to translate.<br>
+/// \param D The declaration to which the attribute is attached.<br>
+/// \param DeclExp An expression involving the Decl to which the attribute<br>
+/// is attached. E.g. the call to a function.<br>
+til::SExpr *SExprBuilder::translateAttrExpr(const Expr *AttrExp,<br>
+ const NamedDecl *D,<br>
+ const Expr *DeclExp,<br>
+ VarDecl *SelfDecl) {<br>
+ // If we are processing a raw attribute expression, with no substitutions.<br>
+ if (!DeclExp)<br>
+ return translateAttrExpr(AttrExp, nullptr);<br>
+<br>
+ CallingContext Ctx(nullptr, D);<br>
+<br>
+ // Examine DeclExp to find SelfArg and FunArgs, which are used to substitute<br>
+ // for formal parameters when we call buildMutexID later.<br>
+ if (const MemberExpr *ME = dyn_cast<MemberExpr>(DeclExp)) {<br>
+ Ctx.SelfArg = ME->getBase();<br>
+ Ctx.SelfArrow = ME->isArrow();<br>
+ } else if (const CXXMemberCallExpr *CE =<br>
+ dyn_cast<CXXMemberCallExpr>(DeclExp)) {<br>
+ Ctx.SelfArg = CE->getImplicitObjectArgument();<br>
+ Ctx.SelfArrow = isCalleeArrow(CE->getCallee());<br>
+ Ctx.NumArgs = CE->getNumArgs();<br>
+ Ctx.FunArgs = CE->getArgs();<br>
+ } else if (const CallExpr *CE = dyn_cast<CallExpr>(DeclExp)) {<br>
+ Ctx.NumArgs = CE->getNumArgs();<br>
+ Ctx.FunArgs = CE->getArgs();<br>
+ } else if (const CXXConstructExpr *CE =<br>
+ dyn_cast<CXXConstructExpr>(DeclExp)) {<br>
+ Ctx.SelfArg = nullptr; // Will be set below<br>
+ Ctx.NumArgs = CE->getNumArgs();<br>
+ Ctx.FunArgs = CE->getArgs();<br>
+ } else if (D && isa<CXXDestructorDecl>(D)) {<br>
+ // There's no such thing as a "destructor call" in the AST.<br>
+ Ctx.SelfArg = DeclExp;<br>
+ }<br>
+<br>
+ // Hack to handle constructors, where self cannot be recovered from<br>
+ // the expression.<br>
+ if (SelfDecl && !Ctx.SelfArg) {<br>
+ DeclRefExpr SelfDRE(SelfDecl, false, SelfDecl->getType(), VK_LValue,<br>
+ SelfDecl->getLocation());<br>
+ Ctx.SelfArg = &SelfDRE;<br>
+<br>
+ // If the attribute has no arguments, then assume the argument is "this".<br>
+ if (!AttrExp)<br>
+ return translateAttrExpr(Ctx.SelfArg, nullptr);<br>
+ else // For most attributes.<br>
+ return translateAttrExpr(AttrExp, &Ctx);<br>
+ }<br>
+<br>
+ // If the attribute has no arguments, then assume the argument is "this".<br>
+ if (!AttrExp)<br>
+ return translateAttrExpr(Ctx.SelfArg, nullptr);<br>
+ else // For most attributes.<br>
+ return translateAttrExpr(AttrExp, &Ctx);<br>
+}<br>
+<br>
+<br>
+/// \brief Translate a clang expression in an attribute to a til::SExpr.<br>
+// This assumes a CallingContext has already been created.<br>
+til::SExpr *SExprBuilder::translateAttrExpr(const Expr *AttrExp,<br>
+ CallingContext *Ctx) {<br>
+ if (const StringLiteral* SLit = dyn_cast_or_null<StringLiteral>(AttrExp)) {<br>
+ if (SLit->getString() == StringRef("*"))<br>
+ // The "*" expr is a universal lock, which essentially turns off<br>
+ // checks until it is removed from the lockset.<br>
+ return new (Arena) til::Wildcard();<br>
+ else<br>
+ // Ignore other string literals for now.<br>
+ return nullptr;<br>
+ }<br>
+<br>
+ til::SExpr *E = translate(AttrExp, Ctx);<br>
+<br>
+ // Hack to deal with smart pointers -- strip off top-level pointer casts.<br>
+ if (auto *CE = dyn_cast_or_null<til::Cast>(E)) {<br>
+ if (CE->castOpcode() == til::CAST_objToPtr)<br>
+ return CE->expr();<br>
+ }<br>
+ return E;<br>
+}<br>
+<br>
+<br>
+<br>
// Translate a clang statement or expression to a TIL expression.<br>
// Also performs substitution of variables; Ctx provides the context.<br>
// Dispatches on the type of S.<br>
@@ -125,9 +221,10 @@ til::SExpr *SExprBuilder::translate(cons<br>
case Stmt::ArraySubscriptExprClass:<br>
return translateArraySubscriptExpr(cast<ArraySubscriptExpr>(S), Ctx);<br>
case Stmt::ConditionalOperatorClass:<br>
- return translateConditionalOperator(cast<ConditionalOperator>(S), Ctx);<br>
+ return translateAbstractConditionalOperator(<br>
+ cast<ConditionalOperator>(S), Ctx);<br>
case Stmt::BinaryConditionalOperatorClass:<br>
- return translateBinaryConditionalOperator(<br>
+ return translateAbstractConditionalOperator(<br>
cast<BinaryConditionalOperator>(S), Ctx);<br>
<br>
// We treat these as no-ops<br>
@@ -162,6 +259,7 @@ til::SExpr *SExprBuilder::translate(cons<br>
}<br>
<br>
<br>
+<br>
til::SExpr *SExprBuilder::translateDeclRefExpr(const DeclRefExpr *DRE,<br>
CallingContext *Ctx) {<br>
const ValueDecl *VD = cast<ValueDecl>(DRE->getDecl()->getCanonicalDecl());<br>
@@ -197,17 +295,72 @@ til::SExpr *SExprBuilder::translateCXXTh<br>
}<br>
<br>
<br>
+const ValueDecl *getValueDeclFromSExpr(const til::SExpr *E) {<br>
+ if (auto *V = dyn_cast<til::Variable>(E))<br>
+ return V->clangDecl();<br>
+ if (auto *P = dyn_cast<til::Project>(E))<br>
+ return P->clangDecl();<br>
+ if (auto *L = dyn_cast<til::LiteralPtr>(E))<br>
+ return L->clangDecl();<br>
+ return 0;<br>
+}<br>
+<br>
+bool hasCppPointerType(const til::SExpr *E) {<br>
+ auto *VD = getValueDeclFromSExpr(E);<br>
+ if (VD && VD->getType()->isPointerType())<br>
+ return true;<br>
+ if (auto *C = dyn_cast<til::Cast>(E))<br>
+ return C->castOpcode() == til::CAST_objToPtr;<br>
+<br>
+ return false;<br>
+}<br>
+<br>
+<br>
+// Grab the very first declaration of virtual method D<br>
+const CXXMethodDecl* getFirstVirtualDecl(const CXXMethodDecl *D) {<br>
+ while (true) {<br>
+ D = D->getCanonicalDecl();<br>
+ CXXMethodDecl::method_iterator I = D->begin_overridden_methods(),<br>
+ E = D->end_overridden_methods();<br>
+ if (I == E)<br>
+ return D; // Method does not override anything<br>
+ D = *I; // FIXME: this does not work with multiple inheritance.<br>
+ }<br>
+ return nullptr;<br>
+}<br>
+<br>
til::SExpr *SExprBuilder::translateMemberExpr(const MemberExpr *ME,<br>
CallingContext *Ctx) {<br>
- til::SExpr *E = translate(ME->getBase(), Ctx);<br>
- E = new (Arena) til::SApply(E);<br>
- return new (Arena) til::Project(E, ME->getMemberDecl());<br>
+ til::SExpr *BE = translate(ME->getBase(), Ctx);<br>
+ til::SExpr *E = new (Arena) til::SApply(BE);<br>
+<br>
+ const ValueDecl *D = ME->getMemberDecl();<br>
+ if (auto *VD = dyn_cast<CXXMethodDecl>(D))<br>
+ D = getFirstVirtualDecl(VD);<br>
+<br>
+ til::Project *P = new (Arena) til::Project(E, D);<br>
+ if (hasCppPointerType(BE))<br>
+ P->setArrow(true);<br>
+ return P;<br>
}<br>
<br>
<br>
til::SExpr *SExprBuilder::translateCallExpr(const CallExpr *CE,<br>
- CallingContext *Ctx) {<br>
- // TODO -- Lock returned<br>
+ CallingContext *Ctx,<br>
+ const Expr *SelfE) {<br>
+ if (CapabilityExprMode) {<br>
+ // Handle LOCK_RETURNED<br>
+ const FunctionDecl *FD = CE->getDirectCallee()->getMostRecentDecl();<br>
+ if (LockReturnedAttr* At = FD->getAttr<LockReturnedAttr>()) {<br>
+ CallingContext LRCallCtx(Ctx);<br>
+ LRCallCtx.AttrDecl = CE->getDirectCallee();<br>
+ LRCallCtx.SelfArg = SelfE;<br>
+ LRCallCtx.NumArgs = CE->getNumArgs();<br>
+ LRCallCtx.FunArgs = CE->getArgs();<br>
+ return translateAttrExpr(At->getArg(), &LRCallCtx);<br>
+ }<br>
+ }<br>
+<br>
til::SExpr *E = translate(CE->getCallee(), Ctx);<br>
for (const auto *Arg : CE->arguments()) {<br>
til::SExpr *A = translate(Arg, Ctx);<br>
@@ -219,12 +372,31 @@ til::SExpr *SExprBuilder::translateCallE<br>
<br>
til::SExpr *SExprBuilder::translateCXXMemberCallExpr(<br>
const CXXMemberCallExpr *ME, CallingContext *Ctx) {<br>
- return translateCallExpr(cast<CallExpr>(ME), Ctx);<br>
+ if (CapabilityExprMode) {<br>
+ // Ignore calls to get() on smart pointers.<br>
+ if (ME->getMethodDecl()->getNameAsString() == "get" &&<br>
+ ME->getNumArgs() == 0) {<br>
+ auto *E = translate(ME->getImplicitObjectArgument(), Ctx);<br>
+ return new (Arena) til::Cast(til::CAST_objToPtr, E);<br>
+ // return E;<br>
+ }<br>
+ }<br>
+ return translateCallExpr(cast<CallExpr>(ME), Ctx,<br>
+ ME->getImplicitObjectArgument());<br>
}<br>
<br>
<br>
til::SExpr *SExprBuilder::translateCXXOperatorCallExpr(<br>
const CXXOperatorCallExpr *OCE, CallingContext *Ctx) {<br>
+ if (CapabilityExprMode) {<br>
+ // Ignore operator * and operator -> on smart pointers.<br>
+ OverloadedOperatorKind k = OCE->getOperator();<br>
+ if (k == OO_Star || k == OO_Arrow) {<br>
+ auto *E = translate(OCE->getArg(0), Ctx);<br>
+ return new (Arena) til::Cast(til::CAST_objToPtr, E);<br>
+ // return E;<br>
+ }<br>
+ }<br>
return translateCallExpr(cast<CallExpr>(OCE), Ctx);<br>
}<br>
<br>
@@ -238,8 +410,23 @@ til::SExpr *SExprBuilder::translateUnary<br>
case UO_PreDec:<br>
return new (Arena) til::Undefined(UO);<br>
<br>
+ case UO_AddrOf: {<br>
+ if (CapabilityExprMode) {<br>
+ // interpret &Graph::mu_ as an existential.<br>
+ if (DeclRefExpr* DRE = dyn_cast<DeclRefExpr>(UO->getSubExpr())) {<br>
+ if (DRE->getDecl()->isCXXInstanceMember()) {<br>
+ // This is a pointer-to-member expression, e.g. &MyClass::mu_.<br>
+ // We interpret this syntax specially, as a wildcard.<br>
+ auto *W = new (Arena) til::Wildcard();<br>
+ return new (Arena) til::Project(W, DRE->getDecl());<br>
+ }<br>
+ }<br>
+ }<br>
+ // otherwise, & is a no-op<br>
+ return translate(UO->getSubExpr(), Ctx);<br>
+ }<br>
+<br>
// We treat these as no-ops<br>
- case UO_AddrOf:<br>
case UO_Deref:<br>
case UO_Plus:<br>
return translate(UO->getSubExpr(), Ctx);<br>
@@ -360,7 +547,9 @@ til::SExpr *SExprBuilder::translateCastE<br>
return E0;<br>
}<br>
til::SExpr *E0 = translate(CE->getSubExpr(), Ctx);<br>
- return new (Arena) til::Load(E0);<br>
+ return E0;<br>
+ // FIXME!! -- get Load working properly<br>
+ // return new (Arena) til::Load(E0);<br>
}<br>
case CK_NoOp:<br>
case CK_DerivedToBase:<br>
@@ -373,6 +562,8 @@ til::SExpr *SExprBuilder::translateCastE<br>
default: {<br>
// FIXME: handle different kinds of casts.<br>
til::SExpr *E0 = translate(CE->getSubExpr(), Ctx);<br>
+ if (CapabilityExprMode)<br>
+ return E0;<br>
return new (Arena) til::Cast(til::CAST_none, E0);<br>
}<br>
}<br>
@@ -389,15 +580,12 @@ SExprBuilder::translateArraySubscriptExp<br>
<br>
<br>
til::SExpr *<br>
-SExprBuilder::translateConditionalOperator(const ConditionalOperator *C,<br>
- CallingContext *Ctx) {<br>
- return new (Arena) til::Undefined(C);<br>
-}<br>
-<br>
-<br>
-til::SExpr *SExprBuilder::translateBinaryConditionalOperator(<br>
- const BinaryConditionalOperator *C, CallingContext *Ctx) {<br>
- return new (Arena) til::Undefined(C);<br>
+SExprBuilder::translateAbstractConditionalOperator(<br>
+ const AbstractConditionalOperator *CO, CallingContext *Ctx) {<br>
+ auto *C = translate(CO->getCond(), Ctx);<br>
+ auto *T = translate(CO->getTrueExpr(), Ctx);<br>
+ auto *E = translate(CO->getFalseExpr(), Ctx);<br>
+ return new (Arena) til::IfThenElse(C, T, E);<br>
}<br>
<br>
<br>
@@ -430,9 +618,7 @@ SExprBuilder::translateDeclStmt(const De<br>
// If E is trivial returns E.<br>
til::SExpr *SExprBuilder::addStatement(til::SExpr* E, const Stmt *S,<br>
const ValueDecl *VD) {<br>
- if (!E)<br>
- return nullptr;<br>
- if (til::ThreadSafetyTIL::isTrivial(E))<br>
+ if (!E || !CurrentBB || til::ThreadSafetyTIL::isTrivial(E))<br>
return E;<br>
<br>
til::Variable *V = new (Arena) til::Variable(E, VD);<br>
@@ -631,7 +817,6 @@ void SExprBuilder::enterCFG(CFG *Cfg, co<br>
BB->reserveInstructions(B->size());<br>
BlockMap[B->getBlockID()] = BB;<br>
}<br>
- CallCtx.reset(new SExprBuilder::CallingContext(D));<br>
<br>
CurrentBB = lookupBlock(&Cfg->getEntry());<br>
auto Parms = isa<ObjCMethodDecl>(D) ? cast<ObjCMethodDecl>(D)->parameters()<br>
@@ -697,7 +882,7 @@ void SExprBuilder::enterCFGBlockBody(con<br>
<br>
<br>
void SExprBuilder::handleStatement(const Stmt *S) {<br>
- til::SExpr *E = translate(S, CallCtx.get());<br>
+ til::SExpr *E = translate(S, nullptr);<br>
addStatement(E, S);<br>
}<br>
<br>
@@ -730,7 +915,7 @@ void SExprBuilder::exitCFGBlockBody(cons<br>
CurrentBB->setTerminator(Tm);<br>
}<br>
else if (N == 2) {<br>
- til::SExpr *C = translate(B->getTerminatorCondition(true), CallCtx.get());<br>
+ til::SExpr *C = translate(B->getTerminatorCondition(true), nullptr);<br>
til::BasicBlock *BB1 = *It ? lookupBlock(*It) : nullptr;<br>
++It;<br>
til::BasicBlock *BB2 = *It ? lookupBlock(*It) : nullptr;<br>
@@ -775,18 +960,15 @@ void SExprBuilder::exitCFG(const CFGBloc<br>
}<br>
<br>
<br>
-<br>
-class TILPrinter : public til::PrettyPrinter<TILPrinter, llvm::raw_ostream> {};<br>
-<br>
-<br>
+/*<br>
void printSCFG(CFGWalker &Walker) {<br>
llvm::BumpPtrAllocator Bpa;<br>
til::MemRegionRef Arena(&Bpa);<br>
- SExprBuilder builder(Arena);<br>
- til::SCFG *Cfg = builder.buildCFG(Walker);<br>
- TILPrinter::print(Cfg, llvm::errs());<br>
+ SExprBuilder SxBuilder(Arena);<br>
+ til::SCFG *Scfg = SxBuilder.buildCFG(Walker);<br>
+ TILPrinter::print(Scfg, llvm::errs());<br>
}<br>
-<br>
+*/<br>
<br>
<br>
} // end namespace threadSafety<br>
<br>
Modified: cfe/trunk/lib/Analysis/ThreadSafetyTIL.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/ThreadSafetyTIL.cpp?rev=214089&r1=214088&r2=214089&view=diff" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/ThreadSafetyTIL.cpp?rev=214089&r1=214088&r2=214089&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/lib/Analysis/ThreadSafetyTIL.cpp (original)<br>
+++ cfe/trunk/lib/Analysis/ThreadSafetyTIL.cpp Mon Jul 28 10:57:27 2014<br>
@@ -88,10 +88,42 @@ void SCFG::renumberVars() {<br>
<br>
<br>
<br>
+// If E is a variable, then trace back through any aliases or redundant<br>
+// Phi nodes to find the canonical definition.<br>
+const SExpr *getCanonicalVal(const SExpr *E) {<br>
+ while (auto *V = dyn_cast<Variable>(E)) {<br>
+ const SExpr *D;<br>
+ do {<br>
+ if (V->kind() != Variable::VK_Let)<br>
+ return V;<br>
+ D = V->definition();<br>
+ auto *V2 = dyn_cast<Variable>(D);<br>
+ if (V2)<br>
+ V = V2;<br>
+ else<br>
+ break;<br>
+ } while (true);<br>
+<br>
+ if (ThreadSafetyTIL::isTrivial(D))<br>
+ return D;<br>
+<br>
+ if (const Phi *Ph = dyn_cast<Phi>(D)) {<br>
+ if (Ph->status() == Phi::PH_SingleVal) {<br>
+ E = Ph->values()[0];<br>
+ continue;<br>
+ }<br>
+ }<br>
+ return V;<br>
+ }<br>
+ return E;<br>
+}<br>
+<br>
+<br>
<br>
// If E is a variable, then trace back through any aliases or redundant<br>
// Phi nodes to find the canonical definition.<br>
-SExpr *getCanonicalVal(SExpr *E) {<br>
+// The non-const version will simplify incomplete Phi nodes.<br>
+SExpr *simplifyToCanonicalVal(SExpr *E) {<br>
while (auto *V = dyn_cast<Variable>(E)) {<br>
SExpr *D;<br>
do {<br>
@@ -123,6 +155,7 @@ SExpr *getCanonicalVal(SExpr *E) {<br>
}<br>
<br>
<br>
+<br>
// Trace the arguments of an incomplete Phi node to see if they have the same<br>
// canonical definition. If so, mark the Phi node as redundant.<br>
// getCanonicalVal() will recursively call simplifyIncompletePhi().<br>
@@ -132,9 +165,9 @@ void simplifyIncompleteArg(Variable *V,<br>
// eliminate infinite recursion -- assume that this node is not redundant.<br>
Ph->setStatus(Phi::PH_MultiVal);<br>
<br>
- SExpr *E0 = getCanonicalVal(Ph->values()[0]);<br>
+ SExpr *E0 = simplifyToCanonicalVal(Ph->values()[0]);<br>
for (unsigned i=1, n=Ph->values().size(); i<n; ++i) {<br>
- SExpr *Ei = getCanonicalVal(Ph->values()[i]);<br>
+ SExpr *Ei = simplifyToCanonicalVal(Ph->values()[i]);<br>
if (Ei == V)<br>
continue; // Recursive reference to itself. Don't count.<br>
if (Ei != E0) {<br>
<br>
Modified: cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp?rev=214089&r1=214088&r2=214089&view=diff" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp?rev=214089&r1=214088&r2=214089&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp (original)<br>
+++ cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp Mon Jul 28 10:57:27 2014<br>
@@ -1444,9 +1444,9 @@ struct SortDiagBySourceLocation {<br>
// -Wthread-safety<br>
//===----------------------------------------------------------------------===//<br>
namespace clang {<br>
-namespace thread_safety {<br>
-namespace {<br>
-class ThreadSafetyReporter : public clang::thread_safety::ThreadSafetyHandler {<br>
+namespace threadSafety {<br>
+<br>
+class ThreadSafetyReporter : public clang::threadSafety::ThreadSafetyHandler {<br>
Sema &S;<br>
DiagList Warnings;<br>
SourceLocation FunLocation, FunEndLocation;<br>
@@ -1608,7 +1608,7 @@ class ThreadSafetyReporter : public clan<br>
Warnings.push_back(DelayedDiag(Warning, OptionalNotes()));<br>
}<br>
};<br>
-}<br>
+<br>
}<br>
}<br>
<br>
@@ -1896,11 +1896,11 @@ AnalysisBasedWarnings::IssueWarnings(sem<br>
if (P.enableThreadSafetyAnalysis) {<br>
SourceLocation FL = AC.getDecl()->getLocation();<br>
SourceLocation FEL = AC.getDecl()->getLocEnd();<br>
- thread_safety::ThreadSafetyReporter Reporter(S, FL, FEL);<br>
+ threadSafety::ThreadSafetyReporter Reporter(S, FL, FEL);<br>
if (!Diags.isIgnored(diag::warn_thread_safety_beta, D->getLocStart()))<br>
Reporter.setIssueBetaWarnings(true);<br>
<br>
- thread_safety::runThreadSafetyAnalysis(AC, Reporter);<br>
+ threadSafety::runThreadSafetyAnalysis(AC, Reporter);<br>
Reporter.emitDiagnostics();<br>
}<br>
<br>
<br>
Modified: cfe/trunk/test/SemaCXX/warn-thread-safety-analysis.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/warn-thread-safety-analysis.cpp?rev=214089&r1=214088&r2=214089&view=diff" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/warn-thread-safety-analysis.cpp?rev=214089&r1=214088&r2=214089&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/test/SemaCXX/warn-thread-safety-analysis.cpp (original)<br>
+++ cfe/trunk/test/SemaCXX/warn-thread-safety-analysis.cpp Mon Jul 28 10:57:27 2014<br>
@@ -95,6 +95,13 @@ public:<br>
};<br>
<br>
<br>
+template <class K, class T><br>
+class MyMap {<br>
+public:<br>
+ T& operator[](const K& k);<br>
+};<br>
+<br>
+<br>
<br>
Mutex sls_mu;<br>
<br>
@@ -2280,6 +2287,15 @@ void test() {<br>
(a > 0 ? fooArray[1] : fooArray[b]).mu_.Lock();<br>
(a > 0 ? fooArray[1] : fooArray[b]).a = 0;<br>
(a > 0 ? fooArray[1] : fooArray[b]).mu_.Unlock();<br>
+}<br>
+<br>
+<br>
+void test2() {<br>
+ Foo *fooArray;<br>
+ Bar bar;<br>
+ int a;<br>
+ int b;<br>
+ int c;<br>
<br>
bar.getFoo().mu_.Lock();<br>
bar.getFooey().a = 0; // \<br>
@@ -2295,20 +2311,20 @@ void test() {<br>
<br>
bar.getFoo3(a, b).mu_.Lock();<br>
bar.getFoo3(a, c).a = 0; // \<br>
- // expected-warning {{writing variable 'a' requires holding mutex 'bar.getFoo3(a,c).mu_' exclusively}} \<br>
- // expected-note {{'bar.getFoo3(a,b).mu_'}}<br>
+ // expected-warning {{writing variable 'a' requires holding mutex 'bar.getFoo3(a, c).mu_' exclusively}} \<br>
+ // expected-note {{found near match 'bar.getFoo3(a, b).mu_'}}<br>
bar.getFoo3(a, b).mu_.Unlock();<br>
<br>
getBarFoo(bar, a).mu_.Lock();<br>
getBarFoo(bar, b).a = 0; // \<br>
- // expected-warning {{writing variable 'a' requires holding mutex 'getBarFoo(bar,b).mu_' exclusively}} \<br>
- // expected-note {{'getBarFoo(bar,a).mu_'}}<br>
+ // expected-warning {{writing variable 'a' requires holding mutex 'getBarFoo(bar, b).mu_' exclusively}} \<br>
+ // expected-note {{found near match 'getBarFoo(bar, a).mu_'}}<br>
getBarFoo(bar, a).mu_.Unlock();<br>
<br>
(a > 0 ? fooArray[1] : fooArray[b]).mu_.Lock();<br>
(a > 0 ? fooArray[b] : fooArray[c]).a = 0; // \<br>
- // expected-warning {{writing variable 'a' requires holding mutex '((a#_)#_#fooArray[b]).mu_' exclusively}} \<br>
- // expected-note {{'((a#_)#_#fooArray[_]).mu_'}}<br>
+ // expected-warning {{writing variable 'a' requires holding mutex '((0 < a) ? fooArray[b] : fooArray[c]).mu_' exclusively}} \<br>
+ // expected-note {{found near match '((0 < a) ? fooArray[1] : fooArray[b]).mu_'}}<br>
(a > 0 ? fooArray[1] : fooArray[b]).mu_.Unlock();<br>
}<br>
<br>
@@ -4378,3 +4394,126 @@ class Foo {<br>
};<br>
<br>
} // end namespace ThreadAttributesOnLambdas<br>
+<br>
+<br>
+<br>
+namespace AttributeExpressionCornerCases {<br>
+<br>
+class Foo {<br>
+ int a GUARDED_BY(getMu());<br>
+<br>
+ Mutex* getMu() LOCK_RETURNED("");<br>
+ Mutex* getUniv() LOCK_RETURNED("*");<br>
+<br>
+ void test1() {<br>
+ a = 0;<br>
+ }<br>
+<br>
+ void test2() EXCLUSIVE_LOCKS_REQUIRED(getUniv()) {<br>
+ a = 0;<br>
+ }<br>
+<br>
+ void foo(Mutex* mu) EXCLUSIVE_LOCKS_REQUIRED(mu);<br>
+<br>
+ void test3() {<br>
+ foo(nullptr);<br>
+ }<br>
+};<br>
+<br>
+<br>
+class MapTest {<br>
+ struct MuCell { Mutex* mu; };<br>
+<br>
+ MyMap<MyString, Mutex*> map;<br>
+ MyMap<MyString, MuCell> mapCell;<br>
+<br>
+ int a GUARDED_BY(map["foo"]);<br>
+ int b GUARDED_BY(mapCell["foo"].mu);<br>
+<br>
+ void test() {<br>
+ map["foo"]->Lock();<br>
+ a = 0;<br>
+ map["foo"]->Unlock();<br>
+ }<br>
+<br>
+ void test2() {<br>
+ mapCell["foo"].mu->Lock();<br>
+ b = 0;<br>
+ mapCell["foo"].mu->Unlock();<br>
+ }<br>
+};<br>
+<br>
+<br>
+class PreciseSmartPtr {<br>
+ SmartPtr<Mutex> mu;<br>
+ int val GUARDED_BY(mu);<br>
+<br>
+ static bool compare(PreciseSmartPtr& a, PreciseSmartPtr &b) {<br>
+ a.mu->Lock();<br>
+ bool result = (a.val == b.val); // expected-warning {{reading variable 'val' requires holding mutex '<a href="http://b.mu" target="_blank">b.mu</a>'}} \<br>
+ // expected-note {{found near match '<a href="http://a.mu" target="_blank">a.mu</a>'}}<br>
+ a.mu->Unlock();<br>
+ return result;<br>
+ }<br>
+};<br>
+<br>
+<br>
+class SmartRedeclare {<br>
+ SmartPtr<Mutex> mu;<br>
+ int val GUARDED_BY(mu);<br>
+<br>
+ void test() EXCLUSIVE_LOCKS_REQUIRED(mu);<br>
+ void test2() EXCLUSIVE_LOCKS_REQUIRED(mu.get());<br>
+ void test3() EXCLUSIVE_LOCKS_REQUIRED(mu.get());<br>
+};<br>
+<br>
+<br>
+void SmartRedeclare::test() EXCLUSIVE_LOCKS_REQUIRED(mu.get()) {<br>
+ val = 0;<br>
+}<br>
+<br>
+void SmartRedeclare::test2() EXCLUSIVE_LOCKS_REQUIRED(mu) {<br>
+ val = 0;<br>
+}<br>
+<br>
+void SmartRedeclare::test3() {<br>
+ val = 0;<br>
+}<br>
+<br>
+<br>
+namespace CustomMutex {<br>
+<br>
+<br>
+class LOCKABLE BaseMutex { };<br>
+class DerivedMutex : public BaseMutex { };<br>
+<br>
+void customLock(const BaseMutex *m) EXCLUSIVE_LOCK_FUNCTION(m);<br>
+void customUnlock(const BaseMutex *m) UNLOCK_FUNCTION(m);<br>
+<br>
+static struct DerivedMutex custMu;<br>
+<br>
+static void doSomethingRequiringLock() EXCLUSIVE_LOCKS_REQUIRED(custMu) { }<br>
+<br>
+void customTest() {<br>
+ customLock(reinterpret_cast<BaseMutex*>(&custMu)); // ignore casts<br>
+ doSomethingRequiringLock();<br>
+ customUnlock(reinterpret_cast<BaseMutex*>(&custMu));<br>
+}<br>
+<br>
+} // end namespace CustomMutex<br>
+<br>
+} // end AttributeExpressionCornerCases<br>
+<br>
+<br>
+namespace ScopedLockReturnedInvalid {<br>
+<br>
+class Opaque;<br>
+<br>
+Mutex* getMutex(Opaque* o) LOCK_RETURNED("");<br>
+<br>
+void test(Opaque* o) {<br>
+ MutexLock lock(getMutex(o));<br>
+}<br>
+<br>
+} // end namespace ScopedLockReturnedInvalid<br>
+<br>
<br>
<br>
_______________________________________________<br>
cfe-commits mailing list<br>
<a href="mailto:cfe-commits@cs.uiuc.edu">cfe-commits@cs.uiuc.edu</a><br>
<a href="http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits" target="_blank">http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits</a><br>
</blockquote></div><br></div>