forked from OSchip/llvm-project
Thread Safety Analysis: Replace the old and broken SExpr with the new
til::SExpr. This is a large patch, with many small changes to pretty printing and expression lowering to make the new SExpr representation equivalent in functionality to the old. llvm-svn: 214089
This commit is contained in:
parent
51510e7f4c
commit
ea1f83385f
|
@ -24,7 +24,7 @@
|
|||
#include "llvm/ADT/StringRef.h"
|
||||
|
||||
namespace clang {
|
||||
namespace thread_safety {
|
||||
namespace threadSafety {
|
||||
|
||||
/// This enum distinguishes between different kinds of operations that may
|
||||
/// need to be protected by locks. We use this enum in error handling.
|
||||
|
@ -190,5 +190,5 @@ void runThreadSafetyAnalysis(AnalysisDeclContext &AC,
|
|||
/// of access.
|
||||
LockKind getLockKindFromAccessKind(AccessKind AK);
|
||||
|
||||
}} // end namespace clang::thread_safety
|
||||
}} // end namespace clang::threadSafety
|
||||
#endif
|
||||
|
|
|
@ -219,18 +219,16 @@ public:
|
|||
/// should be evaluated; multiple calling contexts can be chained together
|
||||
/// by the lock_returned attribute.
|
||||
struct CallingContext {
|
||||
CallingContext *Prev; // The previous context; or 0 if none.
|
||||
const NamedDecl *AttrDecl; // The decl to which the attr is attached.
|
||||
const Expr *SelfArg; // Implicit object argument -- e.g. 'this'
|
||||
unsigned NumArgs; // Number of funArgs
|
||||
const Expr *const *FunArgs; // Function arguments
|
||||
CallingContext *Prev; // The previous context; or 0 if none.
|
||||
bool SelfArrow; // is Self referred to with -> or .?
|
||||
|
||||
CallingContext(const NamedDecl *D = nullptr, const Expr *S = nullptr,
|
||||
unsigned N = 0, const Expr *const *A = nullptr,
|
||||
CallingContext *P = nullptr)
|
||||
: AttrDecl(D), SelfArg(S), NumArgs(N), FunArgs(A), Prev(P),
|
||||
SelfArrow(false)
|
||||
CallingContext(CallingContext *P, const NamedDecl *D = nullptr)
|
||||
: Prev(P), AttrDecl(D), SelfArg(nullptr),
|
||||
NumArgs(0), FunArgs(nullptr), SelfArrow(false)
|
||||
{}
|
||||
};
|
||||
|
||||
|
@ -242,6 +240,13 @@ public:
|
|||
SelfVar->setKind(til::Variable::VK_SFun);
|
||||
}
|
||||
|
||||
// Translate a clang expression in an attribute to a til::SExpr.
|
||||
// Constructs the context from D, DeclExp, and SelfDecl.
|
||||
til::SExpr *translateAttrExpr(const Expr *AttrExp, const NamedDecl *D,
|
||||
const Expr *DeclExp, VarDecl *SelfDecl=nullptr);
|
||||
|
||||
til::SExpr *translateAttrExpr(const Expr *AttrExp, CallingContext *Ctx);
|
||||
|
||||
// Translate a clang statement or expression to a TIL expression.
|
||||
// Also performs substitution of variables; Ctx provides the context.
|
||||
// Dispatches on the type of S.
|
||||
|
@ -262,7 +267,8 @@ private:
|
|||
CallingContext *Ctx) ;
|
||||
til::SExpr *translateCXXThisExpr(const CXXThisExpr *TE, CallingContext *Ctx);
|
||||
til::SExpr *translateMemberExpr(const MemberExpr *ME, CallingContext *Ctx);
|
||||
til::SExpr *translateCallExpr(const CallExpr *CE, CallingContext *Ctx);
|
||||
til::SExpr *translateCallExpr(const CallExpr *CE, CallingContext *Ctx,
|
||||
const Expr *SelfE = nullptr);
|
||||
til::SExpr *translateCXXMemberCallExpr(const CXXMemberCallExpr *ME,
|
||||
CallingContext *Ctx);
|
||||
til::SExpr *translateCXXOperatorCallExpr(const CXXOperatorCallExpr *OCE,
|
||||
|
@ -280,10 +286,8 @@ private:
|
|||
til::SExpr *translateCastExpr(const CastExpr *CE, CallingContext *Ctx);
|
||||
til::SExpr *translateArraySubscriptExpr(const ArraySubscriptExpr *E,
|
||||
CallingContext *Ctx);
|
||||
til::SExpr *translateConditionalOperator(const ConditionalOperator *C,
|
||||
CallingContext *Ctx);
|
||||
til::SExpr *translateBinaryConditionalOperator(
|
||||
const BinaryConditionalOperator *C, CallingContext *Ctx);
|
||||
til::SExpr *translateAbstractConditionalOperator(
|
||||
const AbstractConditionalOperator *C, CallingContext *Ctx);
|
||||
|
||||
til::SExpr *translateDeclStmt(const DeclStmt *S, CallingContext *Ctx);
|
||||
|
||||
|
@ -362,16 +366,19 @@ private:
|
|||
void mergePhiNodesBackEdge(const CFGBlock *Blk);
|
||||
|
||||
private:
|
||||
// Set to true when parsing capability expressions, which get translated
|
||||
// inaccurately in order to hack around smart pointers etc.
|
||||
static const bool CapabilityExprMode = true;
|
||||
|
||||
til::MemRegionRef Arena;
|
||||
til::Variable *SelfVar; // Variable to use for 'this'. May be null.
|
||||
til::SCFG *Scfg;
|
||||
|
||||
til::SCFG *Scfg;
|
||||
StatementMap SMap; // Map from Stmt to TIL Variables
|
||||
LVarIndexMap LVarIdxMap; // Indices of clang local vars.
|
||||
std::vector<til::BasicBlock *> BlockMap; // Map from clang to til BBs.
|
||||
std::vector<BlockInfo> BBInfo; // Extra information per BB.
|
||||
// Indexed by clang BlockID.
|
||||
std::unique_ptr<SExprBuilder::CallingContext> CallCtx; // Root calling context
|
||||
|
||||
LVarDefinitionMap CurrentLVarMap;
|
||||
std::vector<til::Variable*> CurrentArguments;
|
||||
|
|
|
@ -100,6 +100,7 @@ enum TIL_CastOpcode : unsigned char {
|
|||
CAST_truncNum, // truncate precision of numeric type
|
||||
CAST_toFloat, // convert to floating point type
|
||||
CAST_toInt, // convert to integer type
|
||||
CAST_objToPtr // convert smart pointer to pointer (C++ only)
|
||||
};
|
||||
|
||||
const TIL_Opcode COP_Min = COP_Future;
|
||||
|
@ -405,7 +406,8 @@ public:
|
|||
return Vs.reduceVariableRef(this);
|
||||
}
|
||||
|
||||
template <class C> typename C::CType compare(Variable* E, C& Cmp) {
|
||||
template <class C>
|
||||
typename C::CType compare(const Variable* E, C& Cmp) const {
|
||||
return Cmp.compareVariableRefs(this, E);
|
||||
}
|
||||
|
||||
|
@ -455,7 +457,7 @@ public:
|
|||
virtual SExpr *create() { return nullptr; }
|
||||
|
||||
// Return the result of this future if it exists, otherwise return null.
|
||||
SExpr *maybeGetResult() {
|
||||
SExpr *maybeGetResult() const {
|
||||
return Result;
|
||||
}
|
||||
|
||||
|
@ -478,7 +480,8 @@ public:
|
|||
return Vs.traverse(Result, Ctx);
|
||||
}
|
||||
|
||||
template <class C> typename C::CType compare(Future* E, C& Cmp) {
|
||||
template <class C>
|
||||
typename C::CType compare(const Future* E, C& Cmp) const {
|
||||
if (!Result || !E->Result)
|
||||
return Cmp.comparePointers(this, E);
|
||||
return Cmp.compare(Result, E->Result);
|
||||
|
@ -572,8 +575,9 @@ public:
|
|||
return Vs.reduceUndefined(*this);
|
||||
}
|
||||
|
||||
template <class C> typename C::CType compare(Undefined* E, C& Cmp) {
|
||||
return Cmp.comparePointers(Cstmt, E->Cstmt);
|
||||
template <class C>
|
||||
typename C::CType compare(const Undefined* E, C& Cmp) const {
|
||||
return Cmp.trueResult();
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -593,7 +597,8 @@ public:
|
|||
return Vs.reduceWildcard(*this);
|
||||
}
|
||||
|
||||
template <class C> typename C::CType compare(Wildcard* E, C& Cmp) {
|
||||
template <class C>
|
||||
typename C::CType compare(const Wildcard* E, C& Cmp) const {
|
||||
return Cmp.trueResult();
|
||||
}
|
||||
};
|
||||
|
@ -626,9 +631,10 @@ public:
|
|||
|
||||
template <class V> typename V::R_SExpr traverse(V &Vs, typename V::R_Ctx Ctx);
|
||||
|
||||
template <class C> typename C::CType compare(Literal* E, C& Cmp) {
|
||||
// TODO -- use value, not pointer equality
|
||||
return Cmp.comparePointers(Cexpr, E->Cexpr);
|
||||
template <class C>
|
||||
typename C::CType compare(const Literal* E, C& Cmp) const {
|
||||
// TODO: defer actual comparison to LiteralT
|
||||
return Cmp.trueResult();
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -727,7 +733,8 @@ public:
|
|||
return Vs.reduceLiteralPtr(*this);
|
||||
}
|
||||
|
||||
template <class C> typename C::CType compare(LiteralPtr* E, C& Cmp) {
|
||||
template <class C>
|
||||
typename C::CType compare(const LiteralPtr* E, C& Cmp) const {
|
||||
return Cmp.comparePointers(Cvdecl, E->Cvdecl);
|
||||
}
|
||||
|
||||
|
@ -769,7 +776,8 @@ public:
|
|||
return Vs.reduceFunction(*this, Nvd, E1);
|
||||
}
|
||||
|
||||
template <class C> typename C::CType compare(Function* E, C& Cmp) {
|
||||
template <class C>
|
||||
typename C::CType compare(const Function* E, C& Cmp) const {
|
||||
typename C::CType Ct =
|
||||
Cmp.compare(VarDecl->definition(), E->VarDecl->definition());
|
||||
if (Cmp.notTrue(Ct))
|
||||
|
@ -824,7 +832,8 @@ public:
|
|||
return Vs.reduceSFunction(*this, Nvd, E1);
|
||||
}
|
||||
|
||||
template <class C> typename C::CType compare(SFunction* E, C& Cmp) {
|
||||
template <class C>
|
||||
typename C::CType compare(const SFunction* E, C& Cmp) const {
|
||||
Cmp.enterScope(variableDecl(), E->variableDecl());
|
||||
typename C::CType Ct = Cmp.compare(body(), E->body());
|
||||
Cmp.leaveScope();
|
||||
|
@ -859,7 +868,8 @@ public:
|
|||
return Vs.reduceCode(*this, Nt, Nb);
|
||||
}
|
||||
|
||||
template <class C> typename C::CType compare(Code* E, C& Cmp) {
|
||||
template <class C>
|
||||
typename C::CType compare(const Code* E, C& Cmp) const {
|
||||
typename C::CType Ct = Cmp.compare(returnType(), E->returnType());
|
||||
if (Cmp.notTrue(Ct))
|
||||
return Ct;
|
||||
|
@ -894,7 +904,8 @@ public:
|
|||
return Vs.reduceField(*this, Nr, Nb);
|
||||
}
|
||||
|
||||
template <class C> typename C::CType compare(Field* E, C& Cmp) {
|
||||
template <class C>
|
||||
typename C::CType compare(const Field* E, C& Cmp) const {
|
||||
typename C::CType Ct = Cmp.compare(range(), E->range());
|
||||
if (Cmp.notTrue(Ct))
|
||||
return Ct;
|
||||
|
@ -930,7 +941,8 @@ public:
|
|||
return Vs.reduceApply(*this, Nf, Na);
|
||||
}
|
||||
|
||||
template <class C> typename C::CType compare(Apply* E, C& Cmp) {
|
||||
template <class C>
|
||||
typename C::CType compare(const Apply* E, C& Cmp) const {
|
||||
typename C::CType Ct = Cmp.compare(fun(), E->fun());
|
||||
if (Cmp.notTrue(Ct))
|
||||
return Ct;
|
||||
|
@ -958,7 +970,7 @@ public:
|
|||
SExpr *arg() { return Arg.get() ? Arg.get() : Sfun.get(); }
|
||||
const SExpr *arg() const { return Arg.get() ? Arg.get() : Sfun.get(); }
|
||||
|
||||
bool isDelegation() const { return Arg == nullptr; }
|
||||
bool isDelegation() const { return Arg != nullptr; }
|
||||
|
||||
template <class V>
|
||||
typename V::R_SExpr traverse(V &Vs, typename V::R_Ctx Ctx) {
|
||||
|
@ -968,7 +980,8 @@ public:
|
|||
return Vs.reduceSApply(*this, Nf, Na);
|
||||
}
|
||||
|
||||
template <class C> typename C::CType compare(SApply* E, C& Cmp) {
|
||||
template <class C>
|
||||
typename C::CType compare(const SApply* E, C& Cmp) const {
|
||||
typename C::CType Ct = Cmp.compare(sfun(), E->sfun());
|
||||
if (Cmp.notTrue(Ct) || (!arg() && !E->arg()))
|
||||
return Ct;
|
||||
|
@ -989,7 +1002,7 @@ public:
|
|||
Project(SExpr *R, StringRef SName)
|
||||
: SExpr(COP_Project), Rec(R), SlotName(SName), Cvdecl(nullptr)
|
||||
{ }
|
||||
Project(SExpr *R, clang::ValueDecl *Cvd)
|
||||
Project(SExpr *R, const clang::ValueDecl *Cvd)
|
||||
: SExpr(COP_Project), Rec(R), SlotName(Cvd->getName()), Cvdecl(Cvd)
|
||||
{ }
|
||||
Project(const Project &P, SExpr *R)
|
||||
|
@ -999,7 +1012,13 @@ public:
|
|||
SExpr *record() { return Rec.get(); }
|
||||
const SExpr *record() const { return Rec.get(); }
|
||||
|
||||
const clang::ValueDecl *clangValueDecl() const { return Cvdecl; }
|
||||
const clang::ValueDecl *clangDecl() const { return Cvdecl; }
|
||||
|
||||
bool isArrow() const { return (Flags & 0x01) != 0; }
|
||||
void setArrow(bool b) {
|
||||
if (b) Flags |= 0x01;
|
||||
else Flags &= 0xFFFE;
|
||||
}
|
||||
|
||||
StringRef slotName() const {
|
||||
if (Cvdecl)
|
||||
|
@ -1014,7 +1033,8 @@ public:
|
|||
return Vs.reduceProject(*this, Nr);
|
||||
}
|
||||
|
||||
template <class C> typename C::CType compare(Project* E, C& Cmp) {
|
||||
template <class C>
|
||||
typename C::CType compare(const Project* E, C& Cmp) const {
|
||||
typename C::CType Ct = Cmp.compare(record(), E->record());
|
||||
if (Cmp.notTrue(Ct))
|
||||
return Ct;
|
||||
|
@ -1024,7 +1044,7 @@ public:
|
|||
private:
|
||||
SExprRef Rec;
|
||||
StringRef SlotName;
|
||||
clang::ValueDecl *Cvdecl;
|
||||
const clang::ValueDecl *Cvdecl;
|
||||
};
|
||||
|
||||
|
||||
|
@ -1048,7 +1068,8 @@ public:
|
|||
return Vs.reduceCall(*this, Nt);
|
||||
}
|
||||
|
||||
template <class C> typename C::CType compare(Call* E, C& Cmp) {
|
||||
template <class C>
|
||||
typename C::CType compare(const Call* E, C& Cmp) const {
|
||||
return Cmp.compare(target(), E->target());
|
||||
}
|
||||
|
||||
|
@ -1082,7 +1103,8 @@ public:
|
|||
return Vs.reduceAlloc(*this, Nd);
|
||||
}
|
||||
|
||||
template <class C> typename C::CType compare(Alloc* E, C& Cmp) {
|
||||
template <class C>
|
||||
typename C::CType compare(const Alloc* E, C& Cmp) const {
|
||||
typename C::CType Ct = Cmp.compareIntegers(kind(), E->kind());
|
||||
if (Cmp.notTrue(Ct))
|
||||
return Ct;
|
||||
|
@ -1111,7 +1133,8 @@ public:
|
|||
return Vs.reduceLoad(*this, Np);
|
||||
}
|
||||
|
||||
template <class C> typename C::CType compare(Load* E, C& Cmp) {
|
||||
template <class C>
|
||||
typename C::CType compare(const Load* E, C& Cmp) const {
|
||||
return Cmp.compare(pointer(), E->pointer());
|
||||
}
|
||||
|
||||
|
@ -1142,7 +1165,8 @@ public:
|
|||
return Vs.reduceStore(*this, Np, Nv);
|
||||
}
|
||||
|
||||
template <class C> typename C::CType compare(Store* E, C& Cmp) {
|
||||
template <class C>
|
||||
typename C::CType compare(const Store* E, C& Cmp) const {
|
||||
typename C::CType Ct = Cmp.compare(destination(), E->destination());
|
||||
if (Cmp.notTrue(Ct))
|
||||
return Ct;
|
||||
|
@ -1178,7 +1202,8 @@ public:
|
|||
return Vs.reduceArrayIndex(*this, Na, Ni);
|
||||
}
|
||||
|
||||
template <class C> typename C::CType compare(ArrayIndex* E, C& Cmp) {
|
||||
template <class C>
|
||||
typename C::CType compare(const ArrayIndex* E, C& Cmp) const {
|
||||
typename C::CType Ct = Cmp.compare(array(), E->array());
|
||||
if (Cmp.notTrue(Ct))
|
||||
return Ct;
|
||||
|
@ -1215,7 +1240,8 @@ public:
|
|||
return Vs.reduceArrayAdd(*this, Na, Ni);
|
||||
}
|
||||
|
||||
template <class C> typename C::CType compare(ArrayAdd* E, C& Cmp) {
|
||||
template <class C>
|
||||
typename C::CType compare(const ArrayAdd* E, C& Cmp) const {
|
||||
typename C::CType Ct = Cmp.compare(array(), E->array());
|
||||
if (Cmp.notTrue(Ct))
|
||||
return Ct;
|
||||
|
@ -1251,7 +1277,8 @@ public:
|
|||
return Vs.reduceUnaryOp(*this, Ne);
|
||||
}
|
||||
|
||||
template <class C> typename C::CType compare(UnaryOp* E, C& Cmp) {
|
||||
template <class C>
|
||||
typename C::CType compare(const UnaryOp* E, C& Cmp) const {
|
||||
typename C::CType Ct =
|
||||
Cmp.compareIntegers(unaryOpcode(), E->unaryOpcode());
|
||||
if (Cmp.notTrue(Ct))
|
||||
|
@ -1295,7 +1322,8 @@ public:
|
|||
return Vs.reduceBinaryOp(*this, Ne0, Ne1);
|
||||
}
|
||||
|
||||
template <class C> typename C::CType compare(BinaryOp* E, C& Cmp) {
|
||||
template <class C>
|
||||
typename C::CType compare(const BinaryOp* E, C& Cmp) const {
|
||||
typename C::CType Ct =
|
||||
Cmp.compareIntegers(binaryOpcode(), E->binaryOpcode());
|
||||
if (Cmp.notTrue(Ct))
|
||||
|
@ -1333,7 +1361,8 @@ public:
|
|||
return Vs.reduceCast(*this, Ne);
|
||||
}
|
||||
|
||||
template <class C> typename C::CType compare(Cast* E, C& Cmp) {
|
||||
template <class C>
|
||||
typename C::CType compare(const Cast* E, C& Cmp) const {
|
||||
typename C::CType Ct =
|
||||
Cmp.compareIntegers(castOpcode(), E->castOpcode());
|
||||
if (Cmp.notTrue(Ct))
|
||||
|
@ -1386,7 +1415,8 @@ public:
|
|||
return Vs.reducePhi(*this, Nvs);
|
||||
}
|
||||
|
||||
template <class C> typename C::CType compare(Phi *E, C &Cmp) {
|
||||
template <class C>
|
||||
typename C::CType compare(const Phi *E, C &Cmp) const {
|
||||
// TODO: implement CFG comparisons
|
||||
return Cmp.comparePointers(this, E);
|
||||
}
|
||||
|
@ -1503,7 +1533,8 @@ public:
|
|||
return Vs.reduceBasicBlock(*this, Nas, Nis, Nt);
|
||||
}
|
||||
|
||||
template <class C> typename C::CType compare(BasicBlock *E, C &Cmp) {
|
||||
template <class C>
|
||||
typename C::CType compare(const BasicBlock *E, C &Cmp) const {
|
||||
// TODO: implement CFG comparisons
|
||||
return Cmp.comparePointers(this, E);
|
||||
}
|
||||
|
@ -1590,7 +1621,8 @@ public:
|
|||
return Vs.reduceSCFG(*this, Bbs);
|
||||
}
|
||||
|
||||
template <class C> typename C::CType compare(SCFG *E, C &Cmp) {
|
||||
template <class C>
|
||||
typename C::CType compare(const SCFG *E, C &Cmp) const {
|
||||
// TODO -- implement CFG comparisons
|
||||
return Cmp.comparePointers(this, E);
|
||||
}
|
||||
|
@ -1623,7 +1655,8 @@ public:
|
|||
return Vs.reduceGoto(*this, Ntb);
|
||||
}
|
||||
|
||||
template <class C> typename C::CType compare(Goto *E, C &Cmp) {
|
||||
template <class C>
|
||||
typename C::CType compare(const Goto *E, C &Cmp) const {
|
||||
// TODO -- implement CFG comparisons
|
||||
return Cmp.comparePointers(this, E);
|
||||
}
|
||||
|
@ -1668,7 +1701,8 @@ public:
|
|||
return Vs.reduceBranch(*this, Nc, Ntb, Nte);
|
||||
}
|
||||
|
||||
template <class C> typename C::CType compare(Branch *E, C &Cmp) {
|
||||
template <class C>
|
||||
typename C::CType compare(const Branch *E, C &Cmp) const {
|
||||
// TODO -- implement CFG comparisons
|
||||
return Cmp.comparePointers(this, E);
|
||||
}
|
||||
|
@ -1698,7 +1732,8 @@ public:
|
|||
return Vs.reduceIdentifier(*this);
|
||||
}
|
||||
|
||||
template <class C> typename C::CType compare(Identifier* E, C& Cmp) {
|
||||
template <class C>
|
||||
typename C::CType compare(const Identifier* E, C& Cmp) const {
|
||||
return Cmp.compareStrings(name(), E->name());
|
||||
}
|
||||
|
||||
|
@ -1737,7 +1772,8 @@ public:
|
|||
return Vs.reduceIfThenElse(*this, Nc, Nt, Ne);
|
||||
}
|
||||
|
||||
template <class C> typename C::CType compare(IfThenElse* E, C& Cmp) {
|
||||
template <class C>
|
||||
typename C::CType compare(const IfThenElse* E, C& Cmp) const {
|
||||
typename C::CType Ct = Cmp.compare(condition(), E->condition());
|
||||
if (Cmp.notTrue(Ct))
|
||||
return Ct;
|
||||
|
@ -1784,7 +1820,8 @@ public:
|
|||
return Vs.reduceLet(*this, Nvd, E1);
|
||||
}
|
||||
|
||||
template <class C> typename C::CType compare(Let* E, C& Cmp) {
|
||||
template <class C>
|
||||
typename C::CType compare(const Let* E, C& Cmp) const {
|
||||
typename C::CType Ct =
|
||||
Cmp.compare(VarDecl->definition(), E->VarDecl->definition());
|
||||
if (Cmp.notTrue(Ct))
|
||||
|
@ -1802,7 +1839,8 @@ private:
|
|||
|
||||
|
||||
|
||||
SExpr *getCanonicalVal(SExpr *E);
|
||||
const SExpr *getCanonicalVal(const SExpr *E);
|
||||
SExpr* simplifyToCanonicalVal(SExpr *E);
|
||||
void simplifyIncompleteArg(Variable *V, til::Phi *Ph);
|
||||
|
||||
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
|
||||
#include "ThreadSafetyTIL.h"
|
||||
|
||||
#include <ostream>
|
||||
|
||||
namespace clang {
|
||||
namespace threadSafety {
|
||||
namespace til {
|
||||
|
@ -423,7 +425,7 @@ protected:
|
|||
Self *self() { return reinterpret_cast<Self *>(this); }
|
||||
|
||||
public:
|
||||
bool compareByCase(SExpr *E1, SExpr* E2) {
|
||||
bool compareByCase(const SExpr *E1, const SExpr* E2) {
|
||||
switch (E1->opcode()) {
|
||||
#define TIL_OPCODE_DEF(X) \
|
||||
case COP_##X: \
|
||||
|
@ -449,38 +451,86 @@ public:
|
|||
bool compareStrings (StringRef s, StringRef r) { return s == r; }
|
||||
bool comparePointers(const void* P, const void* Q) { return P == Q; }
|
||||
|
||||
bool compare(SExpr *E1, SExpr* E2) {
|
||||
bool compare(const SExpr *E1, const SExpr* E2) {
|
||||
if (E1->opcode() != E2->opcode())
|
||||
return false;
|
||||
return compareByCase(E1, E2);
|
||||
}
|
||||
|
||||
// TODO -- handle alpha-renaming of variables
|
||||
void enterScope(Variable* V1, Variable* V2) { }
|
||||
void enterScope(const Variable* V1, const Variable* V2) { }
|
||||
void leaveScope() { }
|
||||
|
||||
bool compareVariableRefs(Variable* V1, Variable* V2) {
|
||||
bool compareVariableRefs(const Variable* V1, const Variable* V2) {
|
||||
return V1 == V2;
|
||||
}
|
||||
|
||||
static bool compareExprs(SExpr *E1, SExpr* E2) {
|
||||
static bool compareExprs(const SExpr *E1, const SExpr* E2) {
|
||||
EqualsComparator Eq;
|
||||
return Eq.compare(E1, E2);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
class MatchComparator : public Comparator<MatchComparator> {
|
||||
public:
|
||||
// Result type for the comparison, e.g. bool for simple equality,
|
||||
// or int for lexigraphic comparison (-1, 0, 1). Must have one value which
|
||||
// denotes "true".
|
||||
typedef bool CType;
|
||||
|
||||
CType trueResult() { return true; }
|
||||
bool notTrue(CType ct) { return !ct; }
|
||||
|
||||
bool compareIntegers(unsigned i, unsigned j) { return i == j; }
|
||||
bool compareStrings (StringRef s, StringRef r) { return s == r; }
|
||||
bool comparePointers(const void* P, const void* Q) { return P == Q; }
|
||||
|
||||
bool compare(const SExpr *E1, const SExpr* E2) {
|
||||
// Wildcards match anything.
|
||||
if (E1->opcode() == COP_Wildcard || E2->opcode() == COP_Wildcard)
|
||||
return true;
|
||||
// otherwise normal equality.
|
||||
if (E1->opcode() != E2->opcode())
|
||||
return false;
|
||||
return compareByCase(E1, E2);
|
||||
}
|
||||
|
||||
// TODO -- handle alpha-renaming of variables
|
||||
void enterScope(const Variable* V1, const Variable* V2) { }
|
||||
void leaveScope() { }
|
||||
|
||||
bool compareVariableRefs(const Variable* V1, const Variable* V2) {
|
||||
return V1 == V2;
|
||||
}
|
||||
|
||||
static bool compareExprs(const SExpr *E1, const SExpr* E2) {
|
||||
MatchComparator Matcher;
|
||||
return Matcher.compare(E1, E2);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& SS, llvm::StringRef R) {
|
||||
return SS.write(R.data(), R.size());
|
||||
}
|
||||
|
||||
// Pretty printer for TIL expressions
|
||||
template <typename Self, typename StreamType>
|
||||
class PrettyPrinter {
|
||||
private:
|
||||
bool Verbose; // Print out additional information
|
||||
bool Cleanup; // Omit redundant decls.
|
||||
bool CStyle; // Print exprs in C-like syntax.
|
||||
|
||||
public:
|
||||
PrettyPrinter(bool V = false, bool C = true) : Verbose(V), Cleanup(C) { }
|
||||
PrettyPrinter(bool V = false, bool C = true, bool CS = true)
|
||||
: Verbose(V), Cleanup(C), CStyle(CS)
|
||||
{}
|
||||
|
||||
static void print(SExpr *E, StreamType &SS) {
|
||||
static void print(const SExpr *E, StreamType &SS) {
|
||||
Self printer;
|
||||
printer.printSExpr(E, SS, Prec_MAX);
|
||||
}
|
||||
|
@ -502,7 +552,7 @@ protected:
|
|||
static const unsigned Prec_MAX = 6;
|
||||
|
||||
// Return the precedence of a given node, for use in pretty printing.
|
||||
unsigned precedence(SExpr *E) {
|
||||
unsigned precedence(const SExpr *E) {
|
||||
switch (E->opcode()) {
|
||||
case COP_Future: return Prec_Atom;
|
||||
case COP_Undefined: return Prec_Atom;
|
||||
|
@ -529,7 +579,7 @@ protected:
|
|||
|
||||
case COP_UnaryOp: return Prec_Unary;
|
||||
case COP_BinaryOp: return Prec_Binary;
|
||||
case COP_Cast: return Prec_Unary;
|
||||
case COP_Cast: return Prec_Atom;
|
||||
|
||||
case COP_SCFG: return Prec_Decl;
|
||||
case COP_BasicBlock: return Prec_MAX;
|
||||
|
@ -544,7 +594,7 @@ protected:
|
|||
return Prec_MAX;
|
||||
}
|
||||
|
||||
void printBlockLabel(StreamType & SS, BasicBlock *BB, unsigned index) {
|
||||
void printBlockLabel(StreamType & SS, const BasicBlock *BB, unsigned index) {
|
||||
if (!BB) {
|
||||
SS << "BB_null";
|
||||
return;
|
||||
|
@ -555,7 +605,7 @@ protected:
|
|||
SS << index;
|
||||
}
|
||||
|
||||
void printSExpr(SExpr *E, StreamType &SS, unsigned P) {
|
||||
void printSExpr(const SExpr *E, StreamType &SS, unsigned P) {
|
||||
if (!E) {
|
||||
self()->printNull(SS);
|
||||
return;
|
||||
|
@ -582,28 +632,28 @@ protected:
|
|||
SS << "#null";
|
||||
}
|
||||
|
||||
void printFuture(Future *E, StreamType &SS) {
|
||||
void printFuture(const Future *E, StreamType &SS) {
|
||||
self()->printSExpr(E->maybeGetResult(), SS, Prec_Atom);
|
||||
}
|
||||
|
||||
void printUndefined(Undefined *E, StreamType &SS) {
|
||||
void printUndefined(const Undefined *E, StreamType &SS) {
|
||||
SS << "#undefined";
|
||||
}
|
||||
|
||||
void printWildcard(Wildcard *E, StreamType &SS) {
|
||||
SS << "_";
|
||||
void printWildcard(const Wildcard *E, StreamType &SS) {
|
||||
SS << "*";
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void printLiteralT(LiteralT<T> *E, StreamType &SS) {
|
||||
void printLiteralT(const LiteralT<T> *E, StreamType &SS) {
|
||||
SS << E->value();
|
||||
}
|
||||
|
||||
void printLiteralT(LiteralT<uint8_t> *E, StreamType &SS) {
|
||||
void printLiteralT(const LiteralT<uint8_t> *E, StreamType &SS) {
|
||||
SS << "'" << E->value() << "'";
|
||||
}
|
||||
|
||||
void printLiteral(Literal *E, StreamType &SS) {
|
||||
void printLiteral(const Literal *E, StreamType &SS) {
|
||||
if (E->clangExpr()) {
|
||||
SS << getSourceLiteralString(E->clangExpr());
|
||||
return;
|
||||
|
@ -685,13 +735,13 @@ protected:
|
|||
SS << "#lit";
|
||||
}
|
||||
|
||||
void printLiteralPtr(LiteralPtr *E, StreamType &SS) {
|
||||
void printLiteralPtr(const LiteralPtr *E, StreamType &SS) {
|
||||
SS << E->clangDecl()->getNameAsString();
|
||||
}
|
||||
|
||||
void printVariable(Variable *V, StreamType &SS, bool IsVarDecl = false) {
|
||||
void printVariable(const Variable *V, StreamType &SS, bool IsVarDecl = false) {
|
||||
if (!IsVarDecl && Cleanup) {
|
||||
SExpr* E = getCanonicalVal(V);
|
||||
const SExpr* E = getCanonicalVal(V);
|
||||
if (E != V) {
|
||||
printSExpr(E, SS, Prec_Atom);
|
||||
return;
|
||||
|
@ -699,11 +749,13 @@ protected:
|
|||
}
|
||||
if (V->kind() == Variable::VK_LetBB)
|
||||
SS << V->name() << V->getBlockID() << "_" << V->getID();
|
||||
else if (CStyle && V->kind() == Variable::VK_SFun)
|
||||
SS << "this";
|
||||
else
|
||||
SS << V->name() << V->getID();
|
||||
}
|
||||
|
||||
void printFunction(Function *E, StreamType &SS, unsigned sugared = 0) {
|
||||
void printFunction(const Function *E, StreamType &SS, unsigned sugared = 0) {
|
||||
switch (sugared) {
|
||||
default:
|
||||
SS << "\\("; // Lambda
|
||||
|
@ -719,7 +771,7 @@ protected:
|
|||
SS << ": ";
|
||||
self()->printSExpr(E->variableDecl()->definition(), SS, Prec_MAX);
|
||||
|
||||
SExpr *B = E->body();
|
||||
const SExpr *B = E->body();
|
||||
if (B && B->opcode() == COP_Function)
|
||||
self()->printFunction(cast<Function>(B), SS, 2);
|
||||
else {
|
||||
|
@ -728,29 +780,29 @@ protected:
|
|||
}
|
||||
}
|
||||
|
||||
void printSFunction(SFunction *E, StreamType &SS) {
|
||||
void printSFunction(const SFunction *E, StreamType &SS) {
|
||||
SS << "@";
|
||||
self()->printVariable(E->variableDecl(), SS, true);
|
||||
SS << " ";
|
||||
self()->printSExpr(E->body(), SS, Prec_Decl);
|
||||
}
|
||||
|
||||
void printCode(Code *E, StreamType &SS) {
|
||||
void printCode(const Code *E, StreamType &SS) {
|
||||
SS << ": ";
|
||||
self()->printSExpr(E->returnType(), SS, Prec_Decl-1);
|
||||
SS << " -> ";
|
||||
self()->printSExpr(E->body(), SS, Prec_Decl);
|
||||
}
|
||||
|
||||
void printField(Field *E, StreamType &SS) {
|
||||
void printField(const Field *E, StreamType &SS) {
|
||||
SS << ": ";
|
||||
self()->printSExpr(E->range(), SS, Prec_Decl-1);
|
||||
SS << " = ";
|
||||
self()->printSExpr(E->body(), SS, Prec_Decl);
|
||||
}
|
||||
|
||||
void printApply(Apply *E, StreamType &SS, bool sugared = false) {
|
||||
SExpr *F = E->fun();
|
||||
void printApply(const Apply *E, StreamType &SS, bool sugared = false) {
|
||||
const SExpr *F = E->fun();
|
||||
if (F->opcode() == COP_Apply) {
|
||||
printApply(cast<Apply>(F), SS, true);
|
||||
SS << ", ";
|
||||
|
@ -763,7 +815,7 @@ protected:
|
|||
SS << ")$";
|
||||
}
|
||||
|
||||
void printSApply(SApply *E, StreamType &SS) {
|
||||
void printSApply(const SApply *E, StreamType &SS) {
|
||||
self()->printSExpr(E->sfun(), SS, Prec_Postfix);
|
||||
if (E->isDelegation()) {
|
||||
SS << "@(";
|
||||
|
@ -772,14 +824,36 @@ protected:
|
|||
}
|
||||
}
|
||||
|
||||
void printProject(Project *E, StreamType &SS) {
|
||||
void printProject(const Project *E, StreamType &SS) {
|
||||
if (CStyle) {
|
||||
// Omit the this->
|
||||
if (const SApply *SAP = dyn_cast<SApply>(E->record())) {
|
||||
if (const Variable *V = dyn_cast<Variable>(SAP->sfun())) {
|
||||
if (!SAP->isDelegation() && V->kind() == Variable::VK_SFun) {
|
||||
SS << E->slotName();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isa<Wildcard>(E->record())) {
|
||||
// handle existentials
|
||||
SS << "&";
|
||||
SS << E->clangDecl()->getQualifiedNameAsString();
|
||||
return;
|
||||
}
|
||||
}
|
||||
self()->printSExpr(E->record(), SS, Prec_Postfix);
|
||||
SS << ".";
|
||||
if (CStyle && E->isArrow()) {
|
||||
SS << "->";
|
||||
}
|
||||
else {
|
||||
SS << ".";
|
||||
}
|
||||
SS << E->slotName();
|
||||
}
|
||||
|
||||
void printCall(Call *E, StreamType &SS) {
|
||||
SExpr *T = E->target();
|
||||
void printCall(const Call *E, StreamType &SS) {
|
||||
const SExpr *T = E->target();
|
||||
if (T->opcode() == COP_Apply) {
|
||||
self()->printApply(cast<Apply>(T), SS, true);
|
||||
SS << ")";
|
||||
|
@ -790,52 +864,60 @@ protected:
|
|||
}
|
||||
}
|
||||
|
||||
void printAlloc(Alloc *E, StreamType &SS) {
|
||||
void printAlloc(const Alloc *E, StreamType &SS) {
|
||||
SS << "new ";
|
||||
self()->printSExpr(E->dataType(), SS, Prec_Other-1);
|
||||
}
|
||||
|
||||
void printLoad(Load *E, StreamType &SS) {
|
||||
void printLoad(const Load *E, StreamType &SS) {
|
||||
self()->printSExpr(E->pointer(), SS, Prec_Postfix);
|
||||
SS << "^";
|
||||
if (!CStyle)
|
||||
SS << "^";
|
||||
}
|
||||
|
||||
void printStore(Store *E, StreamType &SS) {
|
||||
void printStore(const Store *E, StreamType &SS) {
|
||||
self()->printSExpr(E->destination(), SS, Prec_Other-1);
|
||||
SS << " := ";
|
||||
self()->printSExpr(E->source(), SS, Prec_Other-1);
|
||||
}
|
||||
|
||||
void printArrayIndex(ArrayIndex *E, StreamType &SS) {
|
||||
void printArrayIndex(const ArrayIndex *E, StreamType &SS) {
|
||||
self()->printSExpr(E->array(), SS, Prec_Postfix);
|
||||
SS << "[";
|
||||
self()->printSExpr(E->index(), SS, Prec_MAX);
|
||||
SS << "]";
|
||||
}
|
||||
|
||||
void printArrayAdd(ArrayAdd *E, StreamType &SS) {
|
||||
void printArrayAdd(const ArrayAdd *E, StreamType &SS) {
|
||||
self()->printSExpr(E->array(), SS, Prec_Postfix);
|
||||
SS << " + ";
|
||||
self()->printSExpr(E->index(), SS, Prec_Atom);
|
||||
}
|
||||
|
||||
void printUnaryOp(UnaryOp *E, StreamType &SS) {
|
||||
void printUnaryOp(const UnaryOp *E, StreamType &SS) {
|
||||
SS << getUnaryOpcodeString(E->unaryOpcode());
|
||||
self()->printSExpr(E->expr(), SS, Prec_Unary);
|
||||
}
|
||||
|
||||
void printBinaryOp(BinaryOp *E, StreamType &SS) {
|
||||
void printBinaryOp(const BinaryOp *E, StreamType &SS) {
|
||||
self()->printSExpr(E->expr0(), SS, Prec_Binary-1);
|
||||
SS << " " << getBinaryOpcodeString(E->binaryOpcode()) << " ";
|
||||
self()->printSExpr(E->expr1(), SS, Prec_Binary-1);
|
||||
}
|
||||
|
||||
void printCast(Cast *E, StreamType &SS) {
|
||||
SS << "%";
|
||||
void printCast(const Cast *E, StreamType &SS) {
|
||||
if (!CStyle) {
|
||||
SS << "cast[";
|
||||
SS << E->castOpcode();
|
||||
SS << "](";
|
||||
self()->printSExpr(E->expr(), SS, Prec_Unary);
|
||||
SS << ")";
|
||||
return;
|
||||
}
|
||||
self()->printSExpr(E->expr(), SS, Prec_Unary);
|
||||
}
|
||||
|
||||
void printSCFG(SCFG *E, StreamType &SS) {
|
||||
void printSCFG(const SCFG *E, StreamType &SS) {
|
||||
SS << "CFG {\n";
|
||||
for (auto BBI : *E) {
|
||||
printBasicBlock(BBI, SS);
|
||||
|
@ -844,7 +926,7 @@ protected:
|
|||
newline(SS);
|
||||
}
|
||||
|
||||
void printBasicBlock(BasicBlock *E, StreamType &SS) {
|
||||
void printBasicBlock(const BasicBlock *E, StreamType &SS) {
|
||||
SS << "BB_" << E->blockID() << ":";
|
||||
if (E->parent())
|
||||
SS << " BB_" << E->parent()->blockID();
|
||||
|
@ -867,7 +949,7 @@ protected:
|
|||
SS << ";";
|
||||
newline(SS);
|
||||
}
|
||||
SExpr *T = E->terminator();
|
||||
const SExpr *T = E->terminator();
|
||||
if (T) {
|
||||
self()->printSExpr(T, SS, Prec_MAX);
|
||||
SS << ";";
|
||||
|
@ -876,7 +958,7 @@ protected:
|
|||
newline(SS);
|
||||
}
|
||||
|
||||
void printPhi(Phi *E, StreamType &SS) {
|
||||
void printPhi(const Phi *E, StreamType &SS) {
|
||||
SS << "phi(";
|
||||
if (E->status() == Phi::PH_SingleVal)
|
||||
self()->printSExpr(E->values()[0], SS, Prec_MAX);
|
||||
|
@ -891,12 +973,12 @@ protected:
|
|||
SS << ")";
|
||||
}
|
||||
|
||||
void printGoto(Goto *E, StreamType &SS) {
|
||||
void printGoto(const Goto *E, StreamType &SS) {
|
||||
SS << "goto ";
|
||||
printBlockLabel(SS, E->targetBlock(), E->index());
|
||||
}
|
||||
|
||||
void printBranch(Branch *E, StreamType &SS) {
|
||||
void printBranch(const Branch *E, StreamType &SS) {
|
||||
SS << "branch (";
|
||||
self()->printSExpr(E->condition(), SS, Prec_MAX);
|
||||
SS << ") ";
|
||||
|
@ -905,11 +987,19 @@ protected:
|
|||
printBlockLabel(SS, E->elseBlock(), E->elseIndex());
|
||||
}
|
||||
|
||||
void printIdentifier(Identifier *E, StreamType &SS) {
|
||||
void printIdentifier(const Identifier *E, StreamType &SS) {
|
||||
SS << E->name();
|
||||
}
|
||||
|
||||
void printIfThenElse(IfThenElse *E, StreamType &SS) {
|
||||
void printIfThenElse(const IfThenElse *E, StreamType &SS) {
|
||||
if (CStyle) {
|
||||
printSExpr(E->condition(), SS, Prec_Unary);
|
||||
SS << " ? ";
|
||||
printSExpr(E->thenExpr(), SS, Prec_Unary);
|
||||
SS << " : ";
|
||||
printSExpr(E->elseExpr(), SS, Prec_Unary);
|
||||
return;
|
||||
}
|
||||
SS << "if (";
|
||||
printSExpr(E->condition(), SS, Prec_MAX);
|
||||
SS << ") then ";
|
||||
|
@ -918,7 +1008,7 @@ protected:
|
|||
printSExpr(E->elseExpr(), SS, Prec_Other);
|
||||
}
|
||||
|
||||
void printLet(Let *E, StreamType &SS) {
|
||||
void printLet(const Let *E, StreamType &SS) {
|
||||
SS << "let ";
|
||||
printVariable(E->variableDecl(), SS, true);
|
||||
SS << " = ";
|
||||
|
@ -929,6 +1019,10 @@ protected:
|
|||
};
|
||||
|
||||
|
||||
class StdPrinter : public PrettyPrinter<StdPrinter, std::ostream> { };
|
||||
|
||||
|
||||
|
||||
} // end namespace til
|
||||
} // end namespace threadSafety
|
||||
} // end namespace clang
|
||||
|
|
|
@ -144,7 +144,9 @@ public:
|
|||
}
|
||||
|
||||
iterator begin() { return Data; }
|
||||
const_iterator begin() const { return Data; }
|
||||
iterator end() { return Data + Size; }
|
||||
const_iterator end() const { return Data + Size; }
|
||||
|
||||
const_iterator cbegin() const { return Data; }
|
||||
const_iterator cend() const { return Data + Size; }
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -91,6 +91,102 @@ til::SCFG *SExprBuilder::buildCFG(CFGWalker &Walker) {
|
|||
}
|
||||
|
||||
|
||||
|
||||
inline bool isCalleeArrow(const Expr *E) {
|
||||
const MemberExpr *ME = dyn_cast<MemberExpr>(E->IgnoreParenCasts());
|
||||
return ME ? ME->isArrow() : false;
|
||||
}
|
||||
|
||||
|
||||
/// \brief Translate a clang expression in an attribute to a til::SExpr.
|
||||
/// Constructs the context from D, DeclExp, and SelfDecl.
|
||||
///
|
||||
/// \param AttrExp The expression to translate.
|
||||
/// \param D The declaration to which the attribute is attached.
|
||||
/// \param DeclExp An expression involving the Decl to which the attribute
|
||||
/// is attached. E.g. the call to a function.
|
||||
til::SExpr *SExprBuilder::translateAttrExpr(const Expr *AttrExp,
|
||||
const NamedDecl *D,
|
||||
const Expr *DeclExp,
|
||||
VarDecl *SelfDecl) {
|
||||
// If we are processing a raw attribute expression, with no substitutions.
|
||||
if (!DeclExp)
|
||||
return translateAttrExpr(AttrExp, nullptr);
|
||||
|
||||
CallingContext Ctx(nullptr, D);
|
||||
|
||||
// Examine DeclExp to find SelfArg and FunArgs, which are used to substitute
|
||||
// for formal parameters when we call buildMutexID later.
|
||||
if (const MemberExpr *ME = dyn_cast<MemberExpr>(DeclExp)) {
|
||||
Ctx.SelfArg = ME->getBase();
|
||||
Ctx.SelfArrow = ME->isArrow();
|
||||
} else if (const CXXMemberCallExpr *CE =
|
||||
dyn_cast<CXXMemberCallExpr>(DeclExp)) {
|
||||
Ctx.SelfArg = CE->getImplicitObjectArgument();
|
||||
Ctx.SelfArrow = isCalleeArrow(CE->getCallee());
|
||||
Ctx.NumArgs = CE->getNumArgs();
|
||||
Ctx.FunArgs = CE->getArgs();
|
||||
} else if (const CallExpr *CE = dyn_cast<CallExpr>(DeclExp)) {
|
||||
Ctx.NumArgs = CE->getNumArgs();
|
||||
Ctx.FunArgs = CE->getArgs();
|
||||
} else if (const CXXConstructExpr *CE =
|
||||
dyn_cast<CXXConstructExpr>(DeclExp)) {
|
||||
Ctx.SelfArg = nullptr; // Will be set below
|
||||
Ctx.NumArgs = CE->getNumArgs();
|
||||
Ctx.FunArgs = CE->getArgs();
|
||||
} else if (D && isa<CXXDestructorDecl>(D)) {
|
||||
// There's no such thing as a "destructor call" in the AST.
|
||||
Ctx.SelfArg = DeclExp;
|
||||
}
|
||||
|
||||
// Hack to handle constructors, where self cannot be recovered from
|
||||
// the expression.
|
||||
if (SelfDecl && !Ctx.SelfArg) {
|
||||
DeclRefExpr SelfDRE(SelfDecl, false, SelfDecl->getType(), VK_LValue,
|
||||
SelfDecl->getLocation());
|
||||
Ctx.SelfArg = &SelfDRE;
|
||||
|
||||
// If the attribute has no arguments, then assume the argument is "this".
|
||||
if (!AttrExp)
|
||||
return translateAttrExpr(Ctx.SelfArg, nullptr);
|
||||
else // For most attributes.
|
||||
return translateAttrExpr(AttrExp, &Ctx);
|
||||
}
|
||||
|
||||
// If the attribute has no arguments, then assume the argument is "this".
|
||||
if (!AttrExp)
|
||||
return translateAttrExpr(Ctx.SelfArg, nullptr);
|
||||
else // For most attributes.
|
||||
return translateAttrExpr(AttrExp, &Ctx);
|
||||
}
|
||||
|
||||
|
||||
/// \brief Translate a clang expression in an attribute to a til::SExpr.
|
||||
// This assumes a CallingContext has already been created.
|
||||
til::SExpr *SExprBuilder::translateAttrExpr(const Expr *AttrExp,
|
||||
CallingContext *Ctx) {
|
||||
if (const StringLiteral* SLit = dyn_cast_or_null<StringLiteral>(AttrExp)) {
|
||||
if (SLit->getString() == StringRef("*"))
|
||||
// The "*" expr is a universal lock, which essentially turns off
|
||||
// checks until it is removed from the lockset.
|
||||
return new (Arena) til::Wildcard();
|
||||
else
|
||||
// Ignore other string literals for now.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
til::SExpr *E = translate(AttrExp, Ctx);
|
||||
|
||||
// Hack to deal with smart pointers -- strip off top-level pointer casts.
|
||||
if (auto *CE = dyn_cast_or_null<til::Cast>(E)) {
|
||||
if (CE->castOpcode() == til::CAST_objToPtr)
|
||||
return CE->expr();
|
||||
}
|
||||
return E;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Translate a clang statement or expression to a TIL expression.
|
||||
// Also performs substitution of variables; Ctx provides the context.
|
||||
// Dispatches on the type of S.
|
||||
|
@ -125,9 +221,10 @@ til::SExpr *SExprBuilder::translate(const Stmt *S, CallingContext *Ctx) {
|
|||
case Stmt::ArraySubscriptExprClass:
|
||||
return translateArraySubscriptExpr(cast<ArraySubscriptExpr>(S), Ctx);
|
||||
case Stmt::ConditionalOperatorClass:
|
||||
return translateConditionalOperator(cast<ConditionalOperator>(S), Ctx);
|
||||
return translateAbstractConditionalOperator(
|
||||
cast<ConditionalOperator>(S), Ctx);
|
||||
case Stmt::BinaryConditionalOperatorClass:
|
||||
return translateBinaryConditionalOperator(
|
||||
return translateAbstractConditionalOperator(
|
||||
cast<BinaryConditionalOperator>(S), Ctx);
|
||||
|
||||
// We treat these as no-ops
|
||||
|
@ -162,6 +259,7 @@ til::SExpr *SExprBuilder::translate(const Stmt *S, CallingContext *Ctx) {
|
|||
}
|
||||
|
||||
|
||||
|
||||
til::SExpr *SExprBuilder::translateDeclRefExpr(const DeclRefExpr *DRE,
|
||||
CallingContext *Ctx) {
|
||||
const ValueDecl *VD = cast<ValueDecl>(DRE->getDecl()->getCanonicalDecl());
|
||||
|
@ -197,17 +295,72 @@ til::SExpr *SExprBuilder::translateCXXThisExpr(const CXXThisExpr *TE,
|
|||
}
|
||||
|
||||
|
||||
const ValueDecl *getValueDeclFromSExpr(const til::SExpr *E) {
|
||||
if (auto *V = dyn_cast<til::Variable>(E))
|
||||
return V->clangDecl();
|
||||
if (auto *P = dyn_cast<til::Project>(E))
|
||||
return P->clangDecl();
|
||||
if (auto *L = dyn_cast<til::LiteralPtr>(E))
|
||||
return L->clangDecl();
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool hasCppPointerType(const til::SExpr *E) {
|
||||
auto *VD = getValueDeclFromSExpr(E);
|
||||
if (VD && VD->getType()->isPointerType())
|
||||
return true;
|
||||
if (auto *C = dyn_cast<til::Cast>(E))
|
||||
return C->castOpcode() == til::CAST_objToPtr;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Grab the very first declaration of virtual method D
|
||||
const CXXMethodDecl* getFirstVirtualDecl(const CXXMethodDecl *D) {
|
||||
while (true) {
|
||||
D = D->getCanonicalDecl();
|
||||
CXXMethodDecl::method_iterator I = D->begin_overridden_methods(),
|
||||
E = D->end_overridden_methods();
|
||||
if (I == E)
|
||||
return D; // Method does not override anything
|
||||
D = *I; // FIXME: this does not work with multiple inheritance.
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
til::SExpr *SExprBuilder::translateMemberExpr(const MemberExpr *ME,
|
||||
CallingContext *Ctx) {
|
||||
til::SExpr *E = translate(ME->getBase(), Ctx);
|
||||
E = new (Arena) til::SApply(E);
|
||||
return new (Arena) til::Project(E, ME->getMemberDecl());
|
||||
til::SExpr *BE = translate(ME->getBase(), Ctx);
|
||||
til::SExpr *E = new (Arena) til::SApply(BE);
|
||||
|
||||
const ValueDecl *D = ME->getMemberDecl();
|
||||
if (auto *VD = dyn_cast<CXXMethodDecl>(D))
|
||||
D = getFirstVirtualDecl(VD);
|
||||
|
||||
til::Project *P = new (Arena) til::Project(E, D);
|
||||
if (hasCppPointerType(BE))
|
||||
P->setArrow(true);
|
||||
return P;
|
||||
}
|
||||
|
||||
|
||||
til::SExpr *SExprBuilder::translateCallExpr(const CallExpr *CE,
|
||||
CallingContext *Ctx) {
|
||||
// TODO -- Lock returned
|
||||
CallingContext *Ctx,
|
||||
const Expr *SelfE) {
|
||||
if (CapabilityExprMode) {
|
||||
// Handle LOCK_RETURNED
|
||||
const FunctionDecl *FD = CE->getDirectCallee()->getMostRecentDecl();
|
||||
if (LockReturnedAttr* At = FD->getAttr<LockReturnedAttr>()) {
|
||||
CallingContext LRCallCtx(Ctx);
|
||||
LRCallCtx.AttrDecl = CE->getDirectCallee();
|
||||
LRCallCtx.SelfArg = SelfE;
|
||||
LRCallCtx.NumArgs = CE->getNumArgs();
|
||||
LRCallCtx.FunArgs = CE->getArgs();
|
||||
return translateAttrExpr(At->getArg(), &LRCallCtx);
|
||||
}
|
||||
}
|
||||
|
||||
til::SExpr *E = translate(CE->getCallee(), Ctx);
|
||||
for (const auto *Arg : CE->arguments()) {
|
||||
til::SExpr *A = translate(Arg, Ctx);
|
||||
|
@ -219,12 +372,31 @@ til::SExpr *SExprBuilder::translateCallExpr(const CallExpr *CE,
|
|||
|
||||
til::SExpr *SExprBuilder::translateCXXMemberCallExpr(
|
||||
const CXXMemberCallExpr *ME, CallingContext *Ctx) {
|
||||
return translateCallExpr(cast<CallExpr>(ME), Ctx);
|
||||
if (CapabilityExprMode) {
|
||||
// Ignore calls to get() on smart pointers.
|
||||
if (ME->getMethodDecl()->getNameAsString() == "get" &&
|
||||
ME->getNumArgs() == 0) {
|
||||
auto *E = translate(ME->getImplicitObjectArgument(), Ctx);
|
||||
return new (Arena) til::Cast(til::CAST_objToPtr, E);
|
||||
// return E;
|
||||
}
|
||||
}
|
||||
return translateCallExpr(cast<CallExpr>(ME), Ctx,
|
||||
ME->getImplicitObjectArgument());
|
||||
}
|
||||
|
||||
|
||||
til::SExpr *SExprBuilder::translateCXXOperatorCallExpr(
|
||||
const CXXOperatorCallExpr *OCE, CallingContext *Ctx) {
|
||||
if (CapabilityExprMode) {
|
||||
// Ignore operator * and operator -> on smart pointers.
|
||||
OverloadedOperatorKind k = OCE->getOperator();
|
||||
if (k == OO_Star || k == OO_Arrow) {
|
||||
auto *E = translate(OCE->getArg(0), Ctx);
|
||||
return new (Arena) til::Cast(til::CAST_objToPtr, E);
|
||||
// return E;
|
||||
}
|
||||
}
|
||||
return translateCallExpr(cast<CallExpr>(OCE), Ctx);
|
||||
}
|
||||
|
||||
|
@ -238,8 +410,23 @@ til::SExpr *SExprBuilder::translateUnaryOperator(const UnaryOperator *UO,
|
|||
case UO_PreDec:
|
||||
return new (Arena) til::Undefined(UO);
|
||||
|
||||
case UO_AddrOf: {
|
||||
if (CapabilityExprMode) {
|
||||
// interpret &Graph::mu_ as an existential.
|
||||
if (DeclRefExpr* DRE = dyn_cast<DeclRefExpr>(UO->getSubExpr())) {
|
||||
if (DRE->getDecl()->isCXXInstanceMember()) {
|
||||
// This is a pointer-to-member expression, e.g. &MyClass::mu_.
|
||||
// We interpret this syntax specially, as a wildcard.
|
||||
auto *W = new (Arena) til::Wildcard();
|
||||
return new (Arena) til::Project(W, DRE->getDecl());
|
||||
}
|
||||
}
|
||||
}
|
||||
// otherwise, & is a no-op
|
||||
return translate(UO->getSubExpr(), Ctx);
|
||||
}
|
||||
|
||||
// We treat these as no-ops
|
||||
case UO_AddrOf:
|
||||
case UO_Deref:
|
||||
case UO_Plus:
|
||||
return translate(UO->getSubExpr(), Ctx);
|
||||
|
@ -360,7 +547,9 @@ til::SExpr *SExprBuilder::translateCastExpr(const CastExpr *CE,
|
|||
return E0;
|
||||
}
|
||||
til::SExpr *E0 = translate(CE->getSubExpr(), Ctx);
|
||||
return new (Arena) til::Load(E0);
|
||||
return E0;
|
||||
// FIXME!! -- get Load working properly
|
||||
// return new (Arena) til::Load(E0);
|
||||
}
|
||||
case CK_NoOp:
|
||||
case CK_DerivedToBase:
|
||||
|
@ -373,6 +562,8 @@ til::SExpr *SExprBuilder::translateCastExpr(const CastExpr *CE,
|
|||
default: {
|
||||
// FIXME: handle different kinds of casts.
|
||||
til::SExpr *E0 = translate(CE->getSubExpr(), Ctx);
|
||||
if (CapabilityExprMode)
|
||||
return E0;
|
||||
return new (Arena) til::Cast(til::CAST_none, E0);
|
||||
}
|
||||
}
|
||||
|
@ -389,15 +580,12 @@ SExprBuilder::translateArraySubscriptExpr(const ArraySubscriptExpr *E,
|
|||
|
||||
|
||||
til::SExpr *
|
||||
SExprBuilder::translateConditionalOperator(const ConditionalOperator *C,
|
||||
CallingContext *Ctx) {
|
||||
return new (Arena) til::Undefined(C);
|
||||
}
|
||||
|
||||
|
||||
til::SExpr *SExprBuilder::translateBinaryConditionalOperator(
|
||||
const BinaryConditionalOperator *C, CallingContext *Ctx) {
|
||||
return new (Arena) til::Undefined(C);
|
||||
SExprBuilder::translateAbstractConditionalOperator(
|
||||
const AbstractConditionalOperator *CO, CallingContext *Ctx) {
|
||||
auto *C = translate(CO->getCond(), Ctx);
|
||||
auto *T = translate(CO->getTrueExpr(), Ctx);
|
||||
auto *E = translate(CO->getFalseExpr(), Ctx);
|
||||
return new (Arena) til::IfThenElse(C, T, E);
|
||||
}
|
||||
|
||||
|
||||
|
@ -430,9 +618,7 @@ SExprBuilder::translateDeclStmt(const DeclStmt *S, CallingContext *Ctx) {
|
|||
// If E is trivial returns E.
|
||||
til::SExpr *SExprBuilder::addStatement(til::SExpr* E, const Stmt *S,
|
||||
const ValueDecl *VD) {
|
||||
if (!E)
|
||||
return nullptr;
|
||||
if (til::ThreadSafetyTIL::isTrivial(E))
|
||||
if (!E || !CurrentBB || til::ThreadSafetyTIL::isTrivial(E))
|
||||
return E;
|
||||
|
||||
til::Variable *V = new (Arena) til::Variable(E, VD);
|
||||
|
@ -631,7 +817,6 @@ void SExprBuilder::enterCFG(CFG *Cfg, const NamedDecl *D,
|
|||
BB->reserveInstructions(B->size());
|
||||
BlockMap[B->getBlockID()] = BB;
|
||||
}
|
||||
CallCtx.reset(new SExprBuilder::CallingContext(D));
|
||||
|
||||
CurrentBB = lookupBlock(&Cfg->getEntry());
|
||||
auto Parms = isa<ObjCMethodDecl>(D) ? cast<ObjCMethodDecl>(D)->parameters()
|
||||
|
@ -697,7 +882,7 @@ void SExprBuilder::enterCFGBlockBody(const CFGBlock *B) {
|
|||
|
||||
|
||||
void SExprBuilder::handleStatement(const Stmt *S) {
|
||||
til::SExpr *E = translate(S, CallCtx.get());
|
||||
til::SExpr *E = translate(S, nullptr);
|
||||
addStatement(E, S);
|
||||
}
|
||||
|
||||
|
@ -730,7 +915,7 @@ void SExprBuilder::exitCFGBlockBody(const CFGBlock *B) {
|
|||
CurrentBB->setTerminator(Tm);
|
||||
}
|
||||
else if (N == 2) {
|
||||
til::SExpr *C = translate(B->getTerminatorCondition(true), CallCtx.get());
|
||||
til::SExpr *C = translate(B->getTerminatorCondition(true), nullptr);
|
||||
til::BasicBlock *BB1 = *It ? lookupBlock(*It) : nullptr;
|
||||
++It;
|
||||
til::BasicBlock *BB2 = *It ? lookupBlock(*It) : nullptr;
|
||||
|
@ -775,18 +960,15 @@ void SExprBuilder::exitCFG(const CFGBlock *Last) {
|
|||
}
|
||||
|
||||
|
||||
|
||||
class TILPrinter : public til::PrettyPrinter<TILPrinter, llvm::raw_ostream> {};
|
||||
|
||||
|
||||
/*
|
||||
void printSCFG(CFGWalker &Walker) {
|
||||
llvm::BumpPtrAllocator Bpa;
|
||||
til::MemRegionRef Arena(&Bpa);
|
||||
SExprBuilder builder(Arena);
|
||||
til::SCFG *Cfg = builder.buildCFG(Walker);
|
||||
TILPrinter::print(Cfg, llvm::errs());
|
||||
SExprBuilder SxBuilder(Arena);
|
||||
til::SCFG *Scfg = SxBuilder.buildCFG(Walker);
|
||||
TILPrinter::print(Scfg, llvm::errs());
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
|
||||
} // end namespace threadSafety
|
||||
|
|
|
@ -88,10 +88,42 @@ void SCFG::renumberVars() {
|
|||
|
||||
|
||||
|
||||
// If E is a variable, then trace back through any aliases or redundant
|
||||
// Phi nodes to find the canonical definition.
|
||||
const SExpr *getCanonicalVal(const SExpr *E) {
|
||||
while (auto *V = dyn_cast<Variable>(E)) {
|
||||
const SExpr *D;
|
||||
do {
|
||||
if (V->kind() != Variable::VK_Let)
|
||||
return V;
|
||||
D = V->definition();
|
||||
auto *V2 = dyn_cast<Variable>(D);
|
||||
if (V2)
|
||||
V = V2;
|
||||
else
|
||||
break;
|
||||
} while (true);
|
||||
|
||||
if (ThreadSafetyTIL::isTrivial(D))
|
||||
return D;
|
||||
|
||||
if (const Phi *Ph = dyn_cast<Phi>(D)) {
|
||||
if (Ph->status() == Phi::PH_SingleVal) {
|
||||
E = Ph->values()[0];
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return V;
|
||||
}
|
||||
return E;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// If E is a variable, then trace back through any aliases or redundant
|
||||
// Phi nodes to find the canonical definition.
|
||||
SExpr *getCanonicalVal(SExpr *E) {
|
||||
// The non-const version will simplify incomplete Phi nodes.
|
||||
SExpr *simplifyToCanonicalVal(SExpr *E) {
|
||||
while (auto *V = dyn_cast<Variable>(E)) {
|
||||
SExpr *D;
|
||||
do {
|
||||
|
@ -123,6 +155,7 @@ SExpr *getCanonicalVal(SExpr *E) {
|
|||
}
|
||||
|
||||
|
||||
|
||||
// Trace the arguments of an incomplete Phi node to see if they have the same
|
||||
// canonical definition. If so, mark the Phi node as redundant.
|
||||
// getCanonicalVal() will recursively call simplifyIncompletePhi().
|
||||
|
@ -132,9 +165,9 @@ void simplifyIncompleteArg(Variable *V, til::Phi *Ph) {
|
|||
// eliminate infinite recursion -- assume that this node is not redundant.
|
||||
Ph->setStatus(Phi::PH_MultiVal);
|
||||
|
||||
SExpr *E0 = getCanonicalVal(Ph->values()[0]);
|
||||
SExpr *E0 = simplifyToCanonicalVal(Ph->values()[0]);
|
||||
for (unsigned i=1, n=Ph->values().size(); i<n; ++i) {
|
||||
SExpr *Ei = getCanonicalVal(Ph->values()[i]);
|
||||
SExpr *Ei = simplifyToCanonicalVal(Ph->values()[i]);
|
||||
if (Ei == V)
|
||||
continue; // Recursive reference to itself. Don't count.
|
||||
if (Ei != E0) {
|
||||
|
|
|
@ -1444,9 +1444,9 @@ struct SortDiagBySourceLocation {
|
|||
// -Wthread-safety
|
||||
//===----------------------------------------------------------------------===//
|
||||
namespace clang {
|
||||
namespace thread_safety {
|
||||
namespace {
|
||||
class ThreadSafetyReporter : public clang::thread_safety::ThreadSafetyHandler {
|
||||
namespace threadSafety {
|
||||
|
||||
class ThreadSafetyReporter : public clang::threadSafety::ThreadSafetyHandler {
|
||||
Sema &S;
|
||||
DiagList Warnings;
|
||||
SourceLocation FunLocation, FunEndLocation;
|
||||
|
@ -1608,7 +1608,7 @@ class ThreadSafetyReporter : public clang::thread_safety::ThreadSafetyHandler {
|
|||
Warnings.push_back(DelayedDiag(Warning, OptionalNotes()));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1896,11 +1896,11 @@ AnalysisBasedWarnings::IssueWarnings(sema::AnalysisBasedWarnings::Policy P,
|
|||
if (P.enableThreadSafetyAnalysis) {
|
||||
SourceLocation FL = AC.getDecl()->getLocation();
|
||||
SourceLocation FEL = AC.getDecl()->getLocEnd();
|
||||
thread_safety::ThreadSafetyReporter Reporter(S, FL, FEL);
|
||||
threadSafety::ThreadSafetyReporter Reporter(S, FL, FEL);
|
||||
if (!Diags.isIgnored(diag::warn_thread_safety_beta, D->getLocStart()))
|
||||
Reporter.setIssueBetaWarnings(true);
|
||||
|
||||
thread_safety::runThreadSafetyAnalysis(AC, Reporter);
|
||||
threadSafety::runThreadSafetyAnalysis(AC, Reporter);
|
||||
Reporter.emitDiagnostics();
|
||||
}
|
||||
|
||||
|
|
|
@ -95,6 +95,13 @@ public:
|
|||
};
|
||||
|
||||
|
||||
template <class K, class T>
|
||||
class MyMap {
|
||||
public:
|
||||
T& operator[](const K& k);
|
||||
};
|
||||
|
||||
|
||||
|
||||
Mutex sls_mu;
|
||||
|
||||
|
@ -2280,6 +2287,15 @@ void test() {
|
|||
(a > 0 ? fooArray[1] : fooArray[b]).mu_.Lock();
|
||||
(a > 0 ? fooArray[1] : fooArray[b]).a = 0;
|
||||
(a > 0 ? fooArray[1] : fooArray[b]).mu_.Unlock();
|
||||
}
|
||||
|
||||
|
||||
void test2() {
|
||||
Foo *fooArray;
|
||||
Bar bar;
|
||||
int a;
|
||||
int b;
|
||||
int c;
|
||||
|
||||
bar.getFoo().mu_.Lock();
|
||||
bar.getFooey().a = 0; // \
|
||||
|
@ -2295,20 +2311,20 @@ void test() {
|
|||
|
||||
bar.getFoo3(a, b).mu_.Lock();
|
||||
bar.getFoo3(a, c).a = 0; // \
|
||||
// expected-warning {{writing variable 'a' requires holding mutex 'bar.getFoo3(a,c).mu_' exclusively}} \
|
||||
// expected-note {{'bar.getFoo3(a,b).mu_'}}
|
||||
// expected-warning {{writing variable 'a' requires holding mutex 'bar.getFoo3(a, c).mu_' exclusively}} \
|
||||
// expected-note {{found near match 'bar.getFoo3(a, b).mu_'}}
|
||||
bar.getFoo3(a, b).mu_.Unlock();
|
||||
|
||||
getBarFoo(bar, a).mu_.Lock();
|
||||
getBarFoo(bar, b).a = 0; // \
|
||||
// expected-warning {{writing variable 'a' requires holding mutex 'getBarFoo(bar,b).mu_' exclusively}} \
|
||||
// expected-note {{'getBarFoo(bar,a).mu_'}}
|
||||
// expected-warning {{writing variable 'a' requires holding mutex 'getBarFoo(bar, b).mu_' exclusively}} \
|
||||
// expected-note {{found near match 'getBarFoo(bar, a).mu_'}}
|
||||
getBarFoo(bar, a).mu_.Unlock();
|
||||
|
||||
(a > 0 ? fooArray[1] : fooArray[b]).mu_.Lock();
|
||||
(a > 0 ? fooArray[b] : fooArray[c]).a = 0; // \
|
||||
// expected-warning {{writing variable 'a' requires holding mutex '((a#_)#_#fooArray[b]).mu_' exclusively}} \
|
||||
// expected-note {{'((a#_)#_#fooArray[_]).mu_'}}
|
||||
// expected-warning {{writing variable 'a' requires holding mutex '((0 < a) ? fooArray[b] : fooArray[c]).mu_' exclusively}} \
|
||||
// expected-note {{found near match '((0 < a) ? fooArray[1] : fooArray[b]).mu_'}}
|
||||
(a > 0 ? fooArray[1] : fooArray[b]).mu_.Unlock();
|
||||
}
|
||||
|
||||
|
@ -4378,3 +4394,126 @@ class Foo {
|
|||
};
|
||||
|
||||
} // end namespace ThreadAttributesOnLambdas
|
||||
|
||||
|
||||
|
||||
namespace AttributeExpressionCornerCases {
|
||||
|
||||
class Foo {
|
||||
int a GUARDED_BY(getMu());
|
||||
|
||||
Mutex* getMu() LOCK_RETURNED("");
|
||||
Mutex* getUniv() LOCK_RETURNED("*");
|
||||
|
||||
void test1() {
|
||||
a = 0;
|
||||
}
|
||||
|
||||
void test2() EXCLUSIVE_LOCKS_REQUIRED(getUniv()) {
|
||||
a = 0;
|
||||
}
|
||||
|
||||
void foo(Mutex* mu) EXCLUSIVE_LOCKS_REQUIRED(mu);
|
||||
|
||||
void test3() {
|
||||
foo(nullptr);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class MapTest {
|
||||
struct MuCell { Mutex* mu; };
|
||||
|
||||
MyMap<MyString, Mutex*> map;
|
||||
MyMap<MyString, MuCell> mapCell;
|
||||
|
||||
int a GUARDED_BY(map["foo"]);
|
||||
int b GUARDED_BY(mapCell["foo"].mu);
|
||||
|
||||
void test() {
|
||||
map["foo"]->Lock();
|
||||
a = 0;
|
||||
map["foo"]->Unlock();
|
||||
}
|
||||
|
||||
void test2() {
|
||||
mapCell["foo"].mu->Lock();
|
||||
b = 0;
|
||||
mapCell["foo"].mu->Unlock();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class PreciseSmartPtr {
|
||||
SmartPtr<Mutex> mu;
|
||||
int val GUARDED_BY(mu);
|
||||
|
||||
static bool compare(PreciseSmartPtr& a, PreciseSmartPtr &b) {
|
||||
a.mu->Lock();
|
||||
bool result = (a.val == b.val); // expected-warning {{reading variable 'val' requires holding mutex 'b.mu'}} \
|
||||
// expected-note {{found near match 'a.mu'}}
|
||||
a.mu->Unlock();
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class SmartRedeclare {
|
||||
SmartPtr<Mutex> mu;
|
||||
int val GUARDED_BY(mu);
|
||||
|
||||
void test() EXCLUSIVE_LOCKS_REQUIRED(mu);
|
||||
void test2() EXCLUSIVE_LOCKS_REQUIRED(mu.get());
|
||||
void test3() EXCLUSIVE_LOCKS_REQUIRED(mu.get());
|
||||
};
|
||||
|
||||
|
||||
void SmartRedeclare::test() EXCLUSIVE_LOCKS_REQUIRED(mu.get()) {
|
||||
val = 0;
|
||||
}
|
||||
|
||||
void SmartRedeclare::test2() EXCLUSIVE_LOCKS_REQUIRED(mu) {
|
||||
val = 0;
|
||||
}
|
||||
|
||||
void SmartRedeclare::test3() {
|
||||
val = 0;
|
||||
}
|
||||
|
||||
|
||||
namespace CustomMutex {
|
||||
|
||||
|
||||
class LOCKABLE BaseMutex { };
|
||||
class DerivedMutex : public BaseMutex { };
|
||||
|
||||
void customLock(const BaseMutex *m) EXCLUSIVE_LOCK_FUNCTION(m);
|
||||
void customUnlock(const BaseMutex *m) UNLOCK_FUNCTION(m);
|
||||
|
||||
static struct DerivedMutex custMu;
|
||||
|
||||
static void doSomethingRequiringLock() EXCLUSIVE_LOCKS_REQUIRED(custMu) { }
|
||||
|
||||
void customTest() {
|
||||
customLock(reinterpret_cast<BaseMutex*>(&custMu)); // ignore casts
|
||||
doSomethingRequiringLock();
|
||||
customUnlock(reinterpret_cast<BaseMutex*>(&custMu));
|
||||
}
|
||||
|
||||
} // end namespace CustomMutex
|
||||
|
||||
} // end AttributeExpressionCornerCases
|
||||
|
||||
|
||||
namespace ScopedLockReturnedInvalid {
|
||||
|
||||
class Opaque;
|
||||
|
||||
Mutex* getMutex(Opaque* o) LOCK_RETURNED("");
|
||||
|
||||
void test(Opaque* o) {
|
||||
MutexLock lock(getMutex(o));
|
||||
}
|
||||
|
||||
} // end namespace ScopedLockReturnedInvalid
|
||||
|
||||
|
|
Loading…
Reference in New Issue