41 #include "llvm/ADT/ArrayRef.h" 42 #include "llvm/ADT/DenseMap.h" 43 #include "llvm/ADT/ImmutableMap.h" 44 #include "llvm/ADT/Optional.h" 45 #include "llvm/ADT/PointerIntPair.h" 46 #include "llvm/ADT/STLExtras.h" 47 #include "llvm/ADT/SmallVector.h" 48 #include "llvm/ADT/StringRef.h" 49 #include "llvm/Support/Allocator.h" 50 #include "llvm/Support/Casting.h" 51 #include "llvm/Support/ErrorHandling.h" 52 #include "llvm/Support/raw_ostream.h" 59 #include <type_traits> 63 using namespace clang;
64 using namespace threadSafety;
72 const Expr *DeclExp, StringRef
Kind) {
86 class CapExprSet :
public SmallVector<CapabilityExpr, 4> {
90 iterator It = std::find_if(begin(), end(),
124 bool Asrt,
bool Declrd =
false)
127 virtual ~FactEntry() =
default;
131 bool asserted()
const {
return Asserted; }
132 bool declared()
const {
return Declared; }
134 void setDeclared(
bool D) { Declared = D; }
137 handleRemovalFromIntersection(
const FactSet &FSet, FactManager &FactMan,
140 virtual void handleLock(FactSet &FSet, FactManager &FactMan,
142 StringRef DiagKind)
const = 0;
143 virtual void handleUnlock(FactSet &FSet, FactManager &FactMan,
146 StringRef DiagKind)
const = 0;
154 using FactID =
unsigned short;
160 std::vector<std::unique_ptr<const FactEntry>> Facts;
163 FactID newFact(std::unique_ptr<FactEntry> Entry) {
164 Facts.push_back(std::move(Entry));
165 return static_cast<unsigned short>(Facts.size() - 1);
168 const FactEntry &operator[](FactID F)
const {
return *Facts[F]; }
185 using iterator = FactVec::iterator;
186 using const_iterator = FactVec::const_iterator;
188 iterator begin() {
return FactIDs.begin(); }
189 const_iterator begin()
const {
return FactIDs.begin(); }
191 iterator end() {
return FactIDs.end(); }
192 const_iterator end()
const {
return FactIDs.end(); }
194 bool isEmpty()
const {
return FactIDs.size() == 0; }
197 bool isEmpty(FactManager &FactMan)
const {
198 for (
const auto FID : *
this) {
199 if (!FactMan[FID].negative())
205 void addLockByID(FactID
ID) { FactIDs.push_back(ID); }
207 FactID addLock(FactManager &FM, std::unique_ptr<FactEntry> Entry) {
208 FactID F = FM.newFact(std::move(Entry));
209 FactIDs.push_back(F);
214 unsigned n = FactIDs.size();
218 for (
unsigned i = 0; i < n-1; ++i) {
219 if (FM[FactIDs[i]].
matches(CapE)) {
220 FactIDs[i] = FactIDs[n-1];
225 if (FM[FactIDs[n-1]].
matches(CapE)) {
232 iterator findLockIter(FactManager &FM,
const CapabilityExpr &CapE) {
233 return std::find_if(begin(), end(), [&](FactID ID) {
234 return FM[
ID].matches(CapE);
238 const FactEntry *findLock(FactManager &FM,
const CapabilityExpr &CapE)
const {
239 auto I = std::find_if(begin(), end(), [&](FactID ID) {
240 return FM[
ID].matches(CapE);
242 return I != end() ? &FM[*I] :
nullptr;
245 const FactEntry *findLockUniv(FactManager &FM,
247 auto I = std::find_if(begin(), end(), [&](FactID ID) ->
bool {
248 return FM[
ID].matchesUniv(CapE);
250 return I != end() ? &FM[*I] :
nullptr;
253 const FactEntry *findPartialMatch(FactManager &FM,
255 auto I = std::find_if(begin(), end(), [&](FactID ID) ->
bool {
256 return FM[
ID].partiallyMatches(CapE);
258 return I != end() ? &FM[*I] :
nullptr;
261 bool containsMutexDecl(FactManager &FM,
const ValueDecl* Vd)
const {
262 auto I = std::find_if(begin(), end(), [&](FactID ID) ->
bool {
263 return FM[
ID].valueDecl() == Vd;
269 class ThreadSafetyAnalyzer;
274 namespace threadSafety {
284 BeforeInfo() =
default;
285 BeforeInfo(BeforeInfo &&) =
default;
289 llvm::DenseMap<const ValueDecl *, std::unique_ptr<BeforeInfo>>;
290 using CycleMap = llvm::DenseMap<const ValueDecl *, bool>;
295 BeforeInfo* insertAttrExprs(
const ValueDecl* Vd,
296 ThreadSafetyAnalyzer& Analyzer);
298 BeforeInfo *getBeforeInfoForDecl(
const ValueDecl *Vd,
299 ThreadSafetyAnalyzer &Analyzer);
301 void checkBeforeAfter(
const ValueDecl* Vd,
303 ThreadSafetyAnalyzer& Analyzer,
316 class LocalVariableMap;
318 using LocalVarContext = llvm::ImmutableMap<const NamedDecl *, unsigned>;
326 struct CFGBlockInfo {
334 LocalVarContext EntryContext;
337 LocalVarContext ExitContext;
349 bool Reachable =
false;
352 return Side == CBS_Entry ? EntrySet : ExitSet;
356 return Side == CBS_Entry ? EntryLoc : ExitLoc;
360 CFGBlockInfo(LocalVarContext EmptyCtx)
361 : EntryContext(EmptyCtx), ExitContext(EmptyCtx) {}
364 static CFGBlockInfo getEmptyBlockInfo(LocalVariableMap &M);
380 class LocalVariableMap {
382 using Context = LocalVarContext;
388 struct VarDefinition {
390 friend class LocalVariableMap;
396 const Expr *Exp =
nullptr;
404 bool isReference() {
return !Exp; }
409 : Dec(D), Exp(E), Ctx(C) {}
412 VarDefinition(
const NamedDecl *D,
unsigned R, Context C)
413 : Dec(D), Ref(R), Ctx(C) {}
417 Context::Factory ContextFactory;
418 std::vector<VarDefinition> VarDefinitions;
419 std::vector<unsigned> CtxIndices;
420 std::vector<std::pair<const Stmt *, Context>> SavedContexts;
425 VarDefinitions.push_back(VarDefinition(
nullptr, 0u, getEmptyContext()));
429 const VarDefinition* lookup(
const NamedDecl *D, Context Ctx) {
430 const unsigned *i = Ctx.lookup(D);
433 assert(*i < VarDefinitions.size());
434 return &VarDefinitions[*i];
441 const unsigned *
P = Ctx.lookup(D);
447 if (VarDefinitions[i].Exp) {
448 Ctx = VarDefinitions[i].Ctx;
449 return VarDefinitions[i].Exp;
451 i = VarDefinitions[i].Ref;
456 Context getEmptyContext() {
return ContextFactory.getEmptyMap(); }
461 Context getNextContext(
unsigned &CtxIndex,
const Stmt *S, Context
C) {
462 if (SavedContexts[CtxIndex+1].first == S) {
464 Context Result = SavedContexts[CtxIndex].second;
470 void dumpVarDefinitionName(
unsigned i) {
472 llvm::errs() <<
"Undefined";
475 const NamedDecl *Dec = VarDefinitions[i].Dec;
477 llvm::errs() <<
"<<NULL>>";
481 llvm::errs() <<
"." << i <<
" " << ((
const void*) Dec);
486 for (
unsigned i = 1, e = VarDefinitions.size(); i < e; ++i) {
487 const Expr *Exp = VarDefinitions[i].Exp;
488 unsigned Ref = VarDefinitions[i].Ref;
490 dumpVarDefinitionName(i);
491 llvm::errs() <<
" = ";
492 if (Exp) Exp->
dump();
494 dumpVarDefinitionName(Ref);
495 llvm::errs() <<
"\n";
501 void dumpContext(Context C) {
502 for (Context::iterator I = C.begin(), E = C.end(); I != E; ++I) {
505 const unsigned *i = C.lookup(D);
506 llvm::errs() <<
" -> ";
507 dumpVarDefinitionName(*i);
508 llvm::errs() <<
"\n";
514 std::vector<CFGBlockInfo> &BlockInfo);
517 friend class VarMapBuilder;
520 unsigned getContextIndex() {
return SavedContexts.size()-1; }
523 void saveContext(
const Stmt *S, Context C) {
524 SavedContexts.push_back(std::make_pair(S, C));
529 Context addDefinition(
const NamedDecl *D,
const Expr *Exp, Context Ctx) {
530 assert(!Ctx.contains(D));
531 unsigned newID = VarDefinitions.size();
532 Context NewCtx = ContextFactory.add(Ctx, D, newID);
533 VarDefinitions.push_back(VarDefinition(D, Exp, Ctx));
538 Context addReference(
const NamedDecl *D,
unsigned i, Context Ctx) {
539 unsigned newID = VarDefinitions.size();
540 Context NewCtx = ContextFactory.add(Ctx, D, newID);
541 VarDefinitions.push_back(VarDefinition(D, i, Ctx));
547 Context updateDefinition(
const NamedDecl *D,
Expr *Exp, Context Ctx) {
548 if (Ctx.contains(D)) {
549 unsigned newID = VarDefinitions.size();
550 Context NewCtx = ContextFactory.remove(Ctx, D);
551 NewCtx = ContextFactory.add(NewCtx, D, newID);
552 VarDefinitions.push_back(VarDefinition(D, Exp, Ctx));
560 Context clearDefinition(
const NamedDecl *D, Context Ctx) {
561 Context NewCtx = Ctx;
562 if (NewCtx.contains(D)) {
563 NewCtx = ContextFactory.remove(NewCtx, D);
564 NewCtx = ContextFactory.add(NewCtx, D, 0);
570 Context removeDefinition(
const NamedDecl *D, Context Ctx) {
571 Context NewCtx = Ctx;
572 if (NewCtx.contains(D)) {
573 NewCtx = ContextFactory.remove(NewCtx, D);
578 Context intersectContexts(Context C1, Context C2);
579 Context createReferenceContext(Context C);
580 void intersectBackEdge(Context C1, Context C2);
586 CFGBlockInfo CFGBlockInfo::getEmptyBlockInfo(LocalVariableMap &M) {
587 return CFGBlockInfo(M.getEmptyContext());
595 LocalVariableMap* VMap;
596 LocalVariableMap::Context Ctx;
598 VarMapBuilder(LocalVariableMap *VM, LocalVariableMap::Context C)
599 : VMap(VM), Ctx(C) {}
601 void VisitDeclStmt(
const DeclStmt *S);
608 void VarMapBuilder::VisitDeclStmt(
const DeclStmt *S) {
609 bool modifiedCtx =
false;
611 for (
const auto *D : DGrp) {
612 if (
const auto *VD = dyn_cast_or_null<VarDecl>(D)) {
613 const Expr *E = VD->getInit();
618 Ctx = VMap->addDefinition(VD, E, Ctx);
624 VMap->saveContext(S, Ctx);
628 void VarMapBuilder::VisitBinaryOperator(
const BinaryOperator *BO) {
635 if (
const auto *DRE = dyn_cast<DeclRefExpr>(LHSExp)) {
637 if (Ctx.lookup(VDec)) {
639 Ctx = VMap->updateDefinition(VDec, BO->
getRHS(), Ctx);
642 Ctx = VMap->clearDefinition(VDec, Ctx);
643 VMap->saveContext(BO, Ctx);
651 LocalVariableMap::Context
652 LocalVariableMap::intersectContexts(Context C1, Context C2) {
654 for (
const auto &
P : C1) {
656 const unsigned *i2 = C2.lookup(Dec);
658 Result = removeDefinition(Dec, Result);
659 else if (*i2 !=
P.second)
660 Result = clearDefinition(Dec, Result);
668 LocalVariableMap::Context LocalVariableMap::createReferenceContext(Context C) {
669 Context Result = getEmptyContext();
670 for (
const auto &
P : C)
671 Result = addReference(
P.first,
P.second, Result);
678 void LocalVariableMap::intersectBackEdge(Context C1, Context C2) {
679 for (
const auto &
P : C1) {
680 unsigned i1 =
P.second;
681 VarDefinition *VDef = &VarDefinitions[i1];
682 assert(VDef->isReference());
684 const unsigned *i2 = C2.lookup(
P.first);
685 if (!i2 || (*i2 != i1))
727 void LocalVariableMap::traverseCFG(
CFG *CFGraph,
729 std::vector<CFGBlockInfo> &BlockInfo) {
734 for (
const auto *CurrBlock : *SortedGraph) {
735 unsigned CurrBlockID = CurrBlock->getBlockID();
736 CFGBlockInfo *CurrBlockInfo = &BlockInfo[CurrBlockID];
738 VisitedBlocks.
insert(CurrBlock);
741 bool HasBackEdges =
false;
744 PE = CurrBlock->pred_end(); PI != PE; ++PI) {
746 if (*PI ==
nullptr || !VisitedBlocks.
alreadySet(*PI)) {
751 unsigned PrevBlockID = (*PI)->getBlockID();
752 CFGBlockInfo *PrevBlockInfo = &BlockInfo[PrevBlockID];
755 CurrBlockInfo->EntryContext = PrevBlockInfo->ExitContext;
759 CurrBlockInfo->EntryContext =
760 intersectContexts(CurrBlockInfo->EntryContext,
761 PrevBlockInfo->ExitContext);
768 CurrBlockInfo->EntryContext =
769 createReferenceContext(CurrBlockInfo->EntryContext);
772 saveContext(
nullptr, CurrBlockInfo->EntryContext);
773 CurrBlockInfo->EntryIndex = getContextIndex();
776 VarMapBuilder VMapBuilder(
this, CurrBlockInfo->EntryContext);
777 for (
const auto &BI : *CurrBlock) {
778 switch (BI.getKind()) {
781 VMapBuilder.Visit(CS.
getStmt());
788 CurrBlockInfo->ExitContext = VMapBuilder.Ctx;
792 SE = CurrBlock->succ_end(); SI != SE; ++SI) {
794 if (*SI ==
nullptr || !VisitedBlocks.
alreadySet(*SI))
798 Context LoopBegin = BlockInfo[FirstLoopBlock->
getBlockID()].EntryContext;
799 Context LoopEnd = CurrBlockInfo->ExitContext;
800 intersectBackEdge(LoopBegin, LoopEnd);
806 saveContext(
nullptr, BlockInfo[exitID].ExitContext);
813 std::vector<CFGBlockInfo> &BlockInfo) {
814 for (
const auto *CurrBlock : *SortedGraph) {
815 CFGBlockInfo *CurrBlockInfo = &BlockInfo[CurrBlock->getBlockID()];
819 if (
const Stmt *S = CurrBlock->getTerminator()) {
820 CurrBlockInfo->EntryLoc = CurrBlockInfo->ExitLoc = S->
getBeginLoc();
823 BE = CurrBlock->rend(); BI != BE; ++BI) {
826 CurrBlockInfo->ExitLoc = CS->getStmt()->getBeginLoc();
832 if (CurrBlockInfo->ExitLoc.isValid()) {
835 for (
const auto &BI : *CurrBlock) {
838 CurrBlockInfo->EntryLoc = CS->getStmt()->getBeginLoc();
842 }
else if (CurrBlock->pred_size() == 1 && *CurrBlock->pred_begin() &&
843 CurrBlock != &CFGraph->
getExit()) {
846 CurrBlockInfo->EntryLoc = CurrBlockInfo->ExitLoc =
847 BlockInfo[(*CurrBlock->pred_begin())->getBlockID()].ExitLoc;
854 class LockableFactEntry :
public FactEntry {
861 bool Mng =
false,
bool Asrt =
false)
862 : FactEntry(CE, LK, Loc, Asrt), Managed(Mng) {}
865 handleRemovalFromIntersection(
const FactSet &FSet, FactManager &FactMan,
868 if (!Managed && !asserted() && !negative() && !isUniversal()) {
874 void handleLock(FactSet &FSet, FactManager &FactMan,
const FactEntry &entry,
876 StringRef DiagKind)
const override {
880 void handleUnlock(FactSet &FSet, FactManager &FactMan,
883 StringRef DiagKind)
const override {
884 FSet.removeLock(FactMan, Cp);
886 FSet.addLock(FactMan, llvm::make_unique<LockableFactEntry>(
892 class ScopedLockableFactEntry :
public FactEntry {
894 enum UnderlyingCapabilityKind {
897 UCK_ReleasedExclusive,
900 using UnderlyingCapability =
901 llvm::PointerIntPair<const til::SExpr *, 2, UnderlyingCapabilityKind>;
910 UnderlyingMutexes.emplace_back(M.
sexpr(), UCK_Acquired);
914 UnderlyingMutexes.emplace_back(M.
sexpr(), UCK_Acquired);
918 UnderlyingMutexes.emplace_back(M.
sexpr(), UCK_ReleasedExclusive);
922 UnderlyingMutexes.emplace_back(M.
sexpr(), UCK_ReleasedShared);
926 handleRemovalFromIntersection(
const FactSet &FSet, FactManager &FactMan,
929 for (
const auto &UnderlyingMutex : UnderlyingMutexes) {
930 const auto *Entry = FSet.findLock(
932 if ((UnderlyingMutex.getInt() == UCK_Acquired && Entry) ||
933 (UnderlyingMutex.getInt() != UCK_Acquired && !Entry)) {
937 "mutex",
sx::toString(UnderlyingMutex.getPointer()), loc(), JoinLoc,
943 void handleLock(FactSet &FSet, FactManager &FactMan,
const FactEntry &entry,
945 StringRef DiagKind)
const override {
946 for (
const auto &UnderlyingMutex : UnderlyingMutexes) {
949 if (UnderlyingMutex.getInt() == UCK_Acquired)
950 lock(FSet, FactMan, UnderCp, entry.kind(), entry.loc(), &Handler,
953 unlock(FSet, FactMan, UnderCp, entry.loc(), &Handler, DiagKind);
957 void handleUnlock(FactSet &FSet, FactManager &FactMan,
960 StringRef DiagKind)
const override {
961 assert(!Cp.
negative() &&
"Managing object cannot be negative.");
962 for (
const auto &UnderlyingMutex : UnderlyingMutexes) {
968 if (UnderlyingMutex.getInt() == UCK_Acquired) {
969 unlock(FSet, FactMan, UnderCp, UnlockLoc, TSHandler, DiagKind);
971 LockKind kind = UnderlyingMutex.getInt() == UCK_ReleasedShared
974 lock(FSet, FactMan, UnderCp, kind, UnlockLoc, TSHandler, DiagKind);
978 FSet.removeLock(FactMan, Cp);
982 void lock(FactSet &FSet, FactManager &FactMan,
const CapabilityExpr &Cp,
984 StringRef DiagKind)
const {
985 if (!FSet.findLock(FactMan, Cp)) {
986 FSet.removeLock(FactMan, !Cp);
987 FSet.addLock(FactMan,
988 llvm::make_unique<LockableFactEntry>(Cp, kind, loc));
989 }
else if (Handler) {
994 void unlock(FactSet &FSet, FactManager &FactMan,
const CapabilityExpr &Cp,
996 StringRef DiagKind)
const {
997 if (FSet.findLock(FactMan, Cp)) {
998 FSet.removeLock(FactMan, Cp);
999 FSet.addLock(FactMan, llvm::make_unique<LockableFactEntry>(
1001 }
else if (Handler) {
1008 class ThreadSafetyAnalyzer {
1009 friend class BuildLockset;
1012 llvm::BumpPtrAllocator Bpa;
1018 LocalVariableMap LocalVarMap;
1019 FactManager FactMan;
1020 std::vector<CFGBlockInfo> BlockInfo;
1026 : Arena(&Bpa), SxBuilder(Arena), Handler(H), GlobalBeforeSet(Bset) {}
1030 void addLock(FactSet &FSet, std::unique_ptr<FactEntry> Entry,
1031 StringRef DiagKind,
bool ReqAttr =
false);
1034 StringRef DiagKind);
1036 template <
typename AttrType>
1037 void getMutexIDs(CapExprSet &Mtxs, AttrType *
Attr,
const Expr *Exp,
1040 template <
class AttrType>
1041 void getMutexIDs(CapExprSet &Mtxs, AttrType *Attr,
const Expr *Exp,
1044 Expr *BrE,
bool Neg);
1046 const CallExpr* getTrylockCallExpr(
const Stmt *Cond, LocalVarContext
C,
1049 void getEdgeLockset(FactSet &Result,
const FactSet &ExitSet,
1053 void intersectAndWarn(FactSet &FSet1,
const FactSet &FSet2,
1058 void intersectAndWarn(FactSet &FSet1,
const FactSet &FSet2,
1061 intersectAndWarn(FSet1, FSet2, JoinLoc, LEK1, LEK1, Modify);
1071 ThreadSafetyAnalyzer& Analyzer) {
1073 BeforeInfo *Info =
nullptr;
1077 std::unique_ptr<BeforeInfo> &InfoPtr = BMap[Vd];
1079 InfoPtr.reset(
new BeforeInfo());
1080 Info = InfoPtr.get();
1083 for (
const auto *At : Vd->
attrs()) {
1084 switch (At->getKind()) {
1085 case attr::AcquiredBefore: {
1086 const auto *A = cast<AcquiredBeforeAttr>(At);
1089 for (
const auto *Arg : A->args()) {
1091 Analyzer.SxBuilder.translateAttrExpr(Arg,
nullptr);
1093 Info->Vect.push_back(Cpvd);
1094 const auto It = BMap.find(Cpvd);
1095 if (It == BMap.end())
1096 insertAttrExprs(Cpvd, Analyzer);
1101 case attr::AcquiredAfter: {
1102 const auto *A = cast<AcquiredAfterAttr>(At);
1105 for (
const auto *Arg : A->args()) {
1107 Analyzer.SxBuilder.translateAttrExpr(Arg,
nullptr);
1110 BeforeInfo *ArgInfo = getBeforeInfoForDecl(ArgVd, Analyzer);
1111 ArgInfo->Vect.push_back(Vd);
1124 BeforeSet::BeforeInfo *
1126 ThreadSafetyAnalyzer &Analyzer) {
1127 auto It = BMap.find(Vd);
1128 BeforeInfo *Info =
nullptr;
1129 if (It == BMap.end())
1130 Info = insertAttrExprs(Vd, Analyzer);
1132 Info = It->second.get();
1133 assert(Info &&
"BMap contained nullptr?");
1139 const FactSet& FSet,
1140 ThreadSafetyAnalyzer& Analyzer,
1146 std::function<bool (const ValueDecl*)> traverse = [&](
const ValueDecl* Vd) {
1150 BeforeSet::BeforeInfo *Info = getBeforeInfoForDecl(Vd, Analyzer);
1152 if (Info->Visited == 1)
1155 if (Info->Visited == 2)
1158 if (Info->Vect.empty())
1161 InfoVect.push_back(Info);
1163 for (
const auto *Vdb : Info->Vect) {
1165 if (FSet.containsMutexDecl(Analyzer.FactMan, Vdb)) {
1166 StringRef L1 = StartVd->
getName();
1167 StringRef L2 = Vdb->getName();
1168 Analyzer.Handler.handleLockAcquiredBefore(CapKind, L1, L2, Loc);
1171 if (traverse(Vdb)) {
1172 if (CycMap.find(Vd) == CycMap.end()) {
1173 CycMap.insert(std::make_pair(Vd,
true));
1174 StringRef L1 = Vd->getName();
1175 Analyzer.Handler.handleBeforeAfterCycle(L1, Vd->getLocation());
1185 for (
auto *Info : InfoVect)
1191 if (
const auto *CE = dyn_cast<ImplicitCastExpr>(Exp))
1194 if (
const auto *DR = dyn_cast<DeclRefExpr>(Exp))
1195 return DR->getDecl();
1197 if (
const auto *ME = dyn_cast<MemberExpr>(Exp))
1198 return ME->getMemberDecl();
1205 template <
typename Ty>
1206 class has_arg_iterator_range {
1207 using yes =
char[1];
1210 template <
typename Inner>
1211 static yes& test(Inner *I, decltype(I->args()) * =
nullptr);
1214 static no& test(...);
1217 static const bool value =
sizeof(test<Ty>(
nullptr)) ==
sizeof(yes);
1223 return A->getName();
1231 if (
const auto *RD = RT->getDecl())
1232 if (
const auto *CA = RD->getAttr<CapabilityAttr>())
1235 if (
const auto *TD = TT->getDecl())
1236 if (
const auto *CA = TD->getAttr<CapabilityAttr>())
1245 assert(VD &&
"No ValueDecl passed");
1251 template <
typename AttrTy>
1252 static typename std::enable_if<!has_arg_iterator_range<AttrTy>::value,
1260 template <
typename AttrTy>
1261 static typename std::enable_if<has_arg_iterator_range<AttrTy>::value,
1264 for (
const auto *Arg : A->args()) {
1271 bool ThreadSafetyAnalyzer::inCurrentScope(
const CapabilityExpr &CapE) {
1274 if (
const auto *
P = dyn_cast_or_null<til::Project>(CapE.
sexpr())) {
1275 const auto *VD =
P->clangDecl();
1277 return VD->getDeclContext() == CurrentMethod->getDeclContext();
1284 void ThreadSafetyAnalyzer::addLock(FactSet &FSet,
1285 std::unique_ptr<FactEntry> Entry,
1286 StringRef DiagKind,
bool ReqAttr) {
1287 if (Entry->shouldIgnore())
1290 if (!ReqAttr && !Entry->negative()) {
1293 const FactEntry *Nen = FSet.findLock(FactMan, NegC);
1295 FSet.removeLock(FactMan, NegC);
1298 if (inCurrentScope(*Entry) && !Entry->asserted())
1299 Handler.handleNegativeNotHeld(DiagKind, Entry->toString(),
1305 if (Handler.issueBetaWarnings() &&
1306 !Entry->asserted() && !Entry->declared()) {
1307 GlobalBeforeSet->checkBeforeAfter(Entry->valueDecl(), FSet, *
this,
1308 Entry->loc(), DiagKind);
1312 if (
const FactEntry *Cp = FSet.findLock(FactMan, *Entry)) {
1313 if (!Entry->asserted())
1314 Cp->handleLock(FSet, FactMan, *Entry, Handler, DiagKind);
1316 FSet.addLock(FactMan, std::move(Entry));
1322 void ThreadSafetyAnalyzer::removeLock(FactSet &FSet,
const CapabilityExpr &Cp,
1324 bool FullyRemove,
LockKind ReceivedKind,
1325 StringRef DiagKind) {
1329 const FactEntry *LDat = FSet.findLock(FactMan, Cp);
1331 Handler.handleUnmatchedUnlock(DiagKind, Cp.
toString(), UnlockLoc);
1337 if (ReceivedKind !=
LK_Generic && LDat->kind() != ReceivedKind) {
1338 Handler.handleIncorrectUnlockKind(DiagKind, Cp.
toString(),
1339 LDat->kind(), ReceivedKind, UnlockLoc);
1342 LDat->handleUnlock(FSet, FactMan, Cp, UnlockLoc, FullyRemove, Handler,
1348 template <
typename AttrType>
1349 void ThreadSafetyAnalyzer::getMutexIDs(CapExprSet &Mtxs, AttrType *
Attr,
1352 if (
Attr->args_size() == 0) {
1354 CapabilityExpr Cp = SxBuilder.translateAttrExpr(
nullptr, D, Exp, SelfDecl);
1361 Mtxs.push_back_nodup(Cp);
1365 for (
const auto *Arg :
Attr->args()) {
1366 CapabilityExpr Cp = SxBuilder.translateAttrExpr(Arg, D, Exp, SelfDecl);
1373 Mtxs.push_back_nodup(Cp);
1380 template <
class AttrType>
1381 void ThreadSafetyAnalyzer::getMutexIDs(CapExprSet &Mtxs, AttrType *
Attr,
1385 Expr *BrE,
bool Neg) {
1387 bool branch =
false;
1388 if (
const auto *BLE = dyn_cast_or_null<CXXBoolLiteralExpr>(BrE))
1389 branch = BLE->getValue();
1390 else if (
const auto *ILE = dyn_cast_or_null<IntegerLiteral>(BrE))
1391 branch = ILE->getValue().getBoolValue();
1393 int branchnum = branch ? 0 : 1;
1395 branchnum = !branchnum;
1400 SE = PredBlock->
succ_end(); SI != SE && i < 2; ++SI, ++i) {
1401 if (*SI == CurrBlock && i == branchnum)
1402 getMutexIDs(Mtxs,
Attr, Exp, D);
1407 if (isa<CXXNullPtrLiteralExpr>(E) || isa<GNUNullExpr>(E)) {
1410 }
else if (
const auto *BLE = dyn_cast<CXXBoolLiteralExpr>(E)) {
1411 TCond = BLE->getValue();
1413 }
else if (
const auto *ILE = dyn_cast<IntegerLiteral>(E)) {
1414 TCond = ILE->getValue().getBoolValue();
1416 }
else if (
auto *CE = dyn_cast<ImplicitCastExpr>(E))
1424 const CallExpr* ThreadSafetyAnalyzer::getTrylockCallExpr(
const Stmt *Cond,
1430 if (
const auto *CallExp = dyn_cast<CallExpr>(Cond)) {
1431 if (CallExp->getBuiltinCallee() == Builtin::BI__builtin_expect)
1432 return getTrylockCallExpr(CallExp->getArg(0), C, Negate);
1435 else if (
const auto *PE = dyn_cast<ParenExpr>(Cond))
1436 return getTrylockCallExpr(PE->getSubExpr(), C, Negate);
1437 else if (
const auto *CE = dyn_cast<ImplicitCastExpr>(Cond))
1438 return getTrylockCallExpr(CE->getSubExpr(), C, Negate);
1439 else if (
const auto *FE = dyn_cast<FullExpr>(Cond))
1440 return getTrylockCallExpr(FE->getSubExpr(), C, Negate);
1441 else if (
const auto *DRE = dyn_cast<DeclRefExpr>(Cond)) {
1442 const Expr *E = LocalVarMap.lookupExpr(DRE->getDecl(), C);
1443 return getTrylockCallExpr(E, C, Negate);
1445 else if (
const auto *UOP = dyn_cast<UnaryOperator>(Cond)) {
1446 if (UOP->getOpcode() == UO_LNot) {
1448 return getTrylockCallExpr(UOP->getSubExpr(), C, Negate);
1452 else if (
const auto *BOP = dyn_cast<BinaryOperator>(Cond)) {
1453 if (BOP->getOpcode() == BO_EQ || BOP->getOpcode() == BO_NE) {
1454 if (BOP->getOpcode() == BO_NE)
1459 if (!TCond) Negate = !Negate;
1460 return getTrylockCallExpr(BOP->getLHS(), C, Negate);
1464 if (!TCond) Negate = !Negate;
1465 return getTrylockCallExpr(BOP->getRHS(), C, Negate);
1469 if (BOP->getOpcode() == BO_LAnd) {
1471 return getTrylockCallExpr(BOP->getRHS(), C, Negate);
1473 if (BOP->getOpcode() == BO_LOr)
1474 return getTrylockCallExpr(BOP->getRHS(), C, Negate);
1476 }
else if (
const auto *COP = dyn_cast<ConditionalOperator>(Cond)) {
1480 if (TCond && !FCond)
1481 return getTrylockCallExpr(COP->getCond(), C, Negate);
1482 if (!TCond && FCond) {
1484 return getTrylockCallExpr(COP->getCond(), C, Negate);
1494 void ThreadSafetyAnalyzer::getEdgeLockset(FactSet& Result,
1495 const FactSet &ExitSet,
1502 if (!Cond || isa<ConditionalOperator>(PredBlock->
getTerminator()))
1505 bool Negate =
false;
1506 const CFGBlockInfo *PredBlockInfo = &BlockInfo[PredBlock->
getBlockID()];
1507 const LocalVarContext &LVarCtx = PredBlockInfo->ExitContext;
1508 StringRef CapDiagKind =
"mutex";
1510 const auto *Exp = getTrylockCallExpr(Cond, LVarCtx, Negate);
1514 auto *FunDecl = dyn_cast_or_null<NamedDecl>(Exp->getCalleeDecl());
1515 if(!FunDecl || !FunDecl->hasAttrs())
1518 CapExprSet ExclusiveLocksToAdd;
1519 CapExprSet SharedLocksToAdd;
1522 for (
const auto *
Attr : FunDecl->attrs()) {
1524 case attr::TryAcquireCapability: {
1525 auto *A = cast<TryAcquireCapabilityAttr>(
Attr);
1526 getMutexIDs(A->isShared() ? SharedLocksToAdd : ExclusiveLocksToAdd, A,
1527 Exp, FunDecl, PredBlock, CurrBlock, A->getSuccessValue(),
1532 case attr::ExclusiveTrylockFunction: {
1533 const auto *A = cast<ExclusiveTrylockFunctionAttr>(
Attr);
1534 getMutexIDs(ExclusiveLocksToAdd, A, Exp, FunDecl,
1535 PredBlock, CurrBlock, A->getSuccessValue(), Negate);
1539 case attr::SharedTrylockFunction: {
1540 const auto *A = cast<SharedTrylockFunctionAttr>(
Attr);
1541 getMutexIDs(SharedLocksToAdd, A, Exp, FunDecl,
1542 PredBlock, CurrBlock, A->getSuccessValue(), Negate);
1553 for (
const auto &ExclusiveLockToAdd : ExclusiveLocksToAdd)
1554 addLock(Result, llvm::make_unique<LockableFactEntry>(ExclusiveLockToAdd,
1557 for (
const auto &SharedLockToAdd : SharedLocksToAdd)
1558 addLock(Result, llvm::make_unique<LockableFactEntry>(SharedLockToAdd,
1571 friend class ThreadSafetyAnalyzer;
1573 ThreadSafetyAnalyzer *Analyzer;
1575 LocalVariableMap::Context LVarCtx;
1583 StringRef DiagKind);
1594 bool SkipFirstParam =
false);
1597 BuildLockset(ThreadSafetyAnalyzer *Anlzr, CFGBlockInfo &Info)
1599 LVarCtx(Info.EntryContext), CtxIndex(Info.EntryIndex) {}
1603 void VisitCastExpr(
const CastExpr *CE);
1604 void VisitCallExpr(
const CallExpr *Exp);
1606 void VisitDeclStmt(
const DeclStmt *S);
1613 void BuildLockset::warnIfMutexNotHeld(
const NamedDecl *D,
const Expr *Exp,
1619 CapabilityExpr Cp = Analyzer->SxBuilder.translateAttrExpr(MutexExp, D, Exp);
1629 const FactEntry *LDat = FSet.findLock(Analyzer->FactMan, !Cp);
1631 Analyzer->Handler.handleFunExcludesLock(
1638 if (!Analyzer->inCurrentScope(Cp))
1642 LDat = FSet.findLock(Analyzer->FactMan, Cp);
1644 Analyzer->Handler.handleMutexNotHeld(
"", D, POK, Cp.
toString(),
1650 const FactEntry *LDat = FSet.findLockUniv(Analyzer->FactMan, Cp);
1651 bool NoError =
true;
1654 LDat = FSet.findPartialMatch(Analyzer->FactMan, Cp);
1657 std::string PartMatchStr = LDat->toString();
1658 StringRef PartMatchName(PartMatchStr);
1659 Analyzer->Handler.handleMutexNotHeld(DiagKind, D, POK, Cp.
toString(),
1660 LK, Loc, &PartMatchName);
1663 Analyzer->Handler.handleMutexNotHeld(DiagKind, D, POK, Cp.
toString(),
1669 if (NoError && LDat && !LDat->isAtLeast(LK)) {
1670 Analyzer->Handler.handleMutexNotHeld(DiagKind, D, POK, Cp.
toString(),
1676 void BuildLockset::warnIfMutexHeld(
const NamedDecl *D,
const Expr *Exp,
1677 Expr *MutexExp, StringRef DiagKind) {
1678 CapabilityExpr Cp = Analyzer->SxBuilder.translateAttrExpr(MutexExp, D, Exp);
1686 const FactEntry *LDat = FSet.findLock(Analyzer->FactMan, Cp);
1688 Analyzer->Handler.handleFunExcludesLock(
1706 while (
const auto *DRE = dyn_cast<DeclRefExpr>(Exp)) {
1707 const auto *VD = dyn_cast<
VarDecl>(DRE->getDecl()->getCanonicalDecl());
1708 if (VD && VD->isLocalVarDecl() && VD->getType()->isReferenceType()) {
1709 if (
const auto *E = VD->getInit()) {
1720 if (
const auto *UO = dyn_cast<UnaryOperator>(Exp)) {
1722 if (UO->getOpcode() == UO_Deref)
1723 checkPtAccess(UO->getSubExpr(), AK, POK);
1727 if (
const auto *AE = dyn_cast<ArraySubscriptExpr>(Exp)) {
1728 checkPtAccess(AE->getLHS(), AK, POK);
1732 if (
const auto *ME = dyn_cast<MemberExpr>(Exp)) {
1734 checkPtAccess(ME->getBase(), AK, POK);
1736 checkAccess(ME->getBase(), AK, POK);
1740 if (!D || !D->hasAttrs())
1743 if (D->hasAttr<GuardedVarAttr>() && FSet.isEmpty(Analyzer->FactMan)) {
1744 Analyzer->Handler.handleNoMutexHeld(
"mutex", D, POK, AK, Loc);
1747 for (
const auto *I : D->specific_attrs<GuardedByAttr>())
1748 warnIfMutexNotHeld(D, Exp, AK, I->getArg(), POK,
1757 if (
const auto *PE = dyn_cast<ParenExpr>(Exp)) {
1758 Exp = PE->getSubExpr();
1761 if (
const auto *CE = dyn_cast<CastExpr>(Exp)) {
1762 if (CE->getCastKind() == CK_ArrayToPointerDecay) {
1765 checkAccess(CE->getSubExpr(), AK, POK);
1768 Exp = CE->getSubExpr();
1782 if (D->
hasAttr<PtGuardedVarAttr>() && FSet.isEmpty(Analyzer->FactMan))
1783 Analyzer->Handler.handleNoMutexHeld(
"mutex", D, PtPOK, AK,
1787 warnIfMutexNotHeld(D, Exp, AK, I->getArg(), PtPOK,
1801 void BuildLockset::handleCall(
const Expr *Exp,
const NamedDecl *D,
1804 CapExprSet ExclusiveLocksToAdd, SharedLocksToAdd;
1805 CapExprSet ExclusiveLocksToRemove, SharedLocksToRemove, GenericLocksToRemove;
1806 CapExprSet ScopedExclusiveReqs, ScopedSharedReqs;
1807 StringRef CapDiagKind =
"mutex";
1810 bool isScopedVar =
false;
1812 if (
const auto *CD = dyn_cast<const CXXConstructorDecl>(D)) {
1814 if (PD && PD->
hasAttr<ScopedLockableAttr>())
1820 switch (At->getKind()) {
1823 case attr::AcquireCapability: {
1824 const auto *A = cast<AcquireCapabilityAttr>(At);
1825 Analyzer->getMutexIDs(A->isShared() ? SharedLocksToAdd
1826 : ExclusiveLocksToAdd,
1836 case attr::AssertExclusiveLock: {
1837 const auto *A = cast<AssertExclusiveLockAttr>(At);
1839 CapExprSet AssertLocks;
1840 Analyzer->getMutexIDs(AssertLocks, A, Exp, D, VD);
1841 for (
const auto &AssertLock : AssertLocks)
1842 Analyzer->addLock(FSet,
1843 llvm::make_unique<LockableFactEntry>(
1848 case attr::AssertSharedLock: {
1849 const auto *A = cast<AssertSharedLockAttr>(At);
1851 CapExprSet AssertLocks;
1852 Analyzer->getMutexIDs(AssertLocks, A, Exp, D, VD);
1853 for (
const auto &AssertLock : AssertLocks)
1854 Analyzer->addLock(FSet,
1855 llvm::make_unique<LockableFactEntry>(
1856 AssertLock,
LK_Shared, Loc,
false,
true),
1861 case attr::AssertCapability: {
1862 const auto *A = cast<AssertCapabilityAttr>(At);
1863 CapExprSet AssertLocks;
1864 Analyzer->getMutexIDs(AssertLocks, A, Exp, D, VD);
1865 for (
const auto &AssertLock : AssertLocks)
1866 Analyzer->addLock(FSet,
1867 llvm::make_unique<LockableFactEntry>(
1877 case attr::ReleaseCapability: {
1878 const auto *A = cast<ReleaseCapabilityAttr>(At);
1880 Analyzer->getMutexIDs(GenericLocksToRemove, A, Exp, D, VD);
1881 else if (A->isShared())
1882 Analyzer->getMutexIDs(SharedLocksToRemove, A, Exp, D, VD);
1884 Analyzer->getMutexIDs(ExclusiveLocksToRemove, A, Exp, D, VD);
1890 case attr::RequiresCapability: {
1891 const auto *A = cast<RequiresCapabilityAttr>(At);
1892 for (
auto *Arg : A->args()) {
1898 Analyzer->getMutexIDs(A->isShared() ? ScopedSharedReqs
1899 : ScopedExclusiveReqs,
1906 case attr::LocksExcluded: {
1907 const auto *A = cast<LocksExcludedAttr>(At);
1908 for (
auto *Arg : A->args())
1921 bool Dtor = isa<CXXDestructorDecl>(D);
1922 for (
const auto &M : ExclusiveLocksToRemove)
1923 Analyzer->removeLock(FSet, M, Loc, Dtor,
LK_Exclusive, CapDiagKind);
1924 for (
const auto &M : SharedLocksToRemove)
1925 Analyzer->removeLock(FSet, M, Loc, Dtor,
LK_Shared, CapDiagKind);
1926 for (
const auto &M : GenericLocksToRemove)
1927 Analyzer->removeLock(FSet, M, Loc, Dtor,
LK_Generic, CapDiagKind);
1930 for (
const auto &M : ExclusiveLocksToAdd)
1931 Analyzer->addLock(FSet, llvm::make_unique<LockableFactEntry>(
1934 for (
const auto &M : SharedLocksToAdd)
1935 Analyzer->addLock(FSet, llvm::make_unique<LockableFactEntry>(
1945 CapabilityExpr Scp = Analyzer->SxBuilder.translateAttrExpr(&DRE,
nullptr);
1947 auto ScopedEntry = llvm::make_unique<ScopedLockableFactEntry>(Scp, MLoc);
1948 for (
const auto &M : ExclusiveLocksToAdd)
1949 ScopedEntry->addExclusiveLock(M);
1950 for (
const auto &M : ScopedExclusiveReqs)
1951 ScopedEntry->addExclusiveLock(M);
1952 for (
const auto &M : SharedLocksToAdd)
1953 ScopedEntry->addSharedLock(M);
1954 for (
const auto &M : ScopedSharedReqs)
1955 ScopedEntry->addSharedLock(M);
1956 for (
const auto &M : ExclusiveLocksToRemove)
1957 ScopedEntry->addExclusiveUnlock(M);
1958 for (
const auto &M : SharedLocksToRemove)
1959 ScopedEntry->addSharedUnlock(M);
1960 Analyzer->addLock(FSet, std::move(ScopedEntry), CapDiagKind);
1967 void BuildLockset::VisitUnaryOperator(
const UnaryOperator *UO) {
1983 void BuildLockset::VisitBinaryOperator(
const BinaryOperator *BO) {
1988 LVarCtx = Analyzer->LocalVarMap.getNextContext(CtxIndex, BO, LVarCtx);
1996 void BuildLockset::VisitCastExpr(
const CastExpr *CE) {
2002 void BuildLockset::examineArguments(
const FunctionDecl *FD,
2005 bool SkipFirstParam) {
2015 if (FD->
hasAttr<NoThreadSafetyAnalysisAttr>())
2019 auto Param = Params.begin();
2024 for (
auto Arg = ArgBegin; Param != Params.end() && Arg != ArgEnd;
2032 void BuildLockset::VisitCallExpr(
const CallExpr *Exp) {
2033 if (
const auto *CE = dyn_cast<CXXMemberCallExpr>(Exp)) {
2034 const auto *ME = dyn_cast<
MemberExpr>(CE->getCallee());
2039 if (ME->isArrow()) {
2041 checkPtAccess(CE->getImplicitObjectArgument(),
AK_Read);
2043 checkPtAccess(CE->getImplicitObjectArgument(),
AK_Read);
2046 checkAccess(CE->getImplicitObjectArgument(),
AK_Read);
2048 checkAccess(CE->getImplicitObjectArgument(),
AK_Read);
2052 examineArguments(CE->getDirectCallee(), CE->arg_begin(), CE->arg_end());
2053 }
else if (
const auto *OE = dyn_cast<CXXOperatorCallExpr>(Exp)) {
2054 auto OEop = OE->getOperator();
2058 const Expr *Source = OE->getArg(1);
2066 if (!(OEop == OO_Star && OE->getNumArgs() > 1)) {
2068 checkPtAccess(OE->getArg(0),
AK_Read);
2073 const Expr *Obj = OE->getArg(0);
2079 examineArguments(FD, std::next(OE->arg_begin()), OE->arg_end(),
2080 !isa<CXXMethodDecl>(FD));
2088 auto *D = dyn_cast_or_null<NamedDecl>(Exp->
getCalleeDecl());
2089 if(!D || !D->hasAttrs())
2111 for (
auto *Ctor : RD->
ctors()) {
2112 if (Ctor->isDeleted())
2114 if (Ctor->isMoveConstructor())
2126 CD,
true, Args,
false,
false,
false,
false,
2131 void BuildLockset::VisitDeclStmt(
const DeclStmt *S) {
2133 LVarCtx = Analyzer->LocalVarMap.getNextContext(CtxIndex, S, LVarCtx);
2136 if (
auto *VD = dyn_cast_or_null<VarDecl>(D)) {
2143 if (
auto *EWC = dyn_cast<ExprWithCleanups>(E))
2144 E = EWC->getSubExpr();
2145 if (
auto *BTE = dyn_cast<CXXBindTemporaryExpr>(E))
2146 E = BTE->getSubExpr();
2148 if (
const auto *CE = dyn_cast<CXXConstructExpr>(E)) {
2149 const auto *CtorD = dyn_cast_or_null<NamedDecl>(CE->getConstructor());
2150 if (!CtorD || !CtorD->hasAttrs())
2152 handleCall(E, CtorD, VD);
2153 }
else if (isa<CallExpr>(E) && E->
isRValue()) {
2160 if (!RD || !RD->hasAttr<ScopedLockableAttr>())
2185 void ThreadSafetyAnalyzer::intersectAndWarn(FactSet &FSet1,
2186 const FactSet &FSet2,
2191 FactSet FSet1Orig = FSet1;
2194 for (
const auto &Fact : FSet2) {
2195 const FactEntry *LDat1 =
nullptr;
2196 const FactEntry *LDat2 = &FactMan[Fact];
2197 FactSet::iterator Iter1 = FSet1.findLockIter(FactMan, *LDat2);
2198 if (Iter1 != FSet1.end()) LDat1 = &FactMan[*Iter1];
2201 if (LDat1->kind() != LDat2->kind()) {
2202 Handler.handleExclusiveAndShared(
"mutex", LDat2->toString(),
2203 LDat2->loc(), LDat1->loc());
2209 else if (Modify && LDat1->asserted() && !LDat2->asserted()) {
2214 LDat2->handleRemovalFromIntersection(FSet2, FactMan, JoinLoc, LEK1,
2220 for (
const auto &Fact : FSet1Orig) {
2221 const FactEntry *LDat1 = &FactMan[Fact];
2222 const FactEntry *LDat2 = FSet2.findLock(FactMan, *LDat1);
2225 LDat1->handleRemovalFromIntersection(FSet1Orig, FactMan, JoinLoc, LEK2,
2228 FSet1.removeLock(FactMan, *LDat1);
2242 if (isa<CXXThrowExpr>(S->getStmt()))
2257 if (!walker.
init(AC))
2265 const auto *CurrentFunction = dyn_cast<
FunctionDecl>(D);
2268 if (D->
hasAttr<NoThreadSafetyAnalysisAttr>())
2275 if (isa<CXXConstructorDecl>(D))
2277 if (isa<CXXDestructorDecl>(D))
2280 Handler.enterFunction(CurrentFunction);
2283 CFGBlockInfo::getEmptyBlockInfo(LocalVarMap));
2295 LocalVarMap.traverseCFG(CFGraph, SortedGraph, BlockInfo);
2300 CapExprSet ExclusiveLocksAcquired;
2301 CapExprSet SharedLocksAcquired;
2302 CapExprSet LocksReleased;
2309 FactSet &InitialLockset = BlockInfo[FirstBlock->
getBlockID()].EntrySet;
2311 CapExprSet ExclusiveLocksToAdd;
2312 CapExprSet SharedLocksToAdd;
2313 StringRef CapDiagKind =
"mutex";
2318 if (
const auto *A = dyn_cast<RequiresCapabilityAttr>(
Attr)) {
2319 getMutexIDs(A->isShared() ? SharedLocksToAdd : ExclusiveLocksToAdd, A,
2322 }
else if (
const auto *A = dyn_cast<ReleaseCapabilityAttr>(
Attr)) {
2325 if (A->args_size() == 0)
2327 getMutexIDs(A->isShared() ? SharedLocksToAdd : ExclusiveLocksToAdd, A,
2329 getMutexIDs(LocksReleased, A,
nullptr, D);
2331 }
else if (
const auto *A = dyn_cast<AcquireCapabilityAttr>(
Attr)) {
2332 if (A->args_size() == 0)
2334 getMutexIDs(A->isShared() ? SharedLocksAcquired
2335 : ExclusiveLocksAcquired,
2338 }
else if (isa<ExclusiveTrylockFunctionAttr>(
Attr)) {
2341 }
else if (isa<SharedTrylockFunctionAttr>(
Attr)) {
2344 }
else if (isa<TryAcquireCapabilityAttr>(
Attr)) {
2351 for (
const auto &Mu : ExclusiveLocksToAdd) {
2352 auto Entry = llvm::make_unique<LockableFactEntry>(Mu,
LK_Exclusive, Loc);
2353 Entry->setDeclared(
true);
2354 addLock(InitialLockset, std::move(Entry), CapDiagKind,
true);
2356 for (
const auto &Mu : SharedLocksToAdd) {
2357 auto Entry = llvm::make_unique<LockableFactEntry>(Mu,
LK_Shared, Loc);
2358 Entry->setDeclared(
true);
2359 addLock(InitialLockset, std::move(Entry), CapDiagKind,
true);
2363 for (
const auto *CurrBlock : *SortedGraph) {
2364 unsigned CurrBlockID = CurrBlock->
getBlockID();
2365 CFGBlockInfo *CurrBlockInfo = &BlockInfo[CurrBlockID];
2368 VisitedBlocks.
insert(CurrBlock);
2383 bool LocksetInitialized =
false;
2386 PE = CurrBlock->
pred_end(); PI != PE; ++PI) {
2388 if (*PI ==
nullptr || !VisitedBlocks.
alreadySet(*PI))
2391 unsigned PrevBlockID = (*PI)->getBlockID();
2392 CFGBlockInfo *PrevBlockInfo = &BlockInfo[PrevBlockID];
2399 CurrBlockInfo->Reachable =
true;
2405 if (
const Stmt *Terminator = (*PI)->getTerminator()) {
2406 if (isa<ContinueStmt>(Terminator) || isa<BreakStmt>(Terminator)) {
2407 SpecialBlocks.push_back(*PI);
2412 FactSet PrevLockset;
2413 getEdgeLockset(PrevLockset, PrevBlockInfo->ExitSet, *PI, CurrBlock);
2415 if (!LocksetInitialized) {
2416 CurrBlockInfo->EntrySet = PrevLockset;
2417 LocksetInitialized =
true;
2419 intersectAndWarn(CurrBlockInfo->EntrySet, PrevLockset,
2420 CurrBlockInfo->EntryLoc,
2426 if (!CurrBlockInfo->Reachable)
2431 for (
const auto *PrevBlock : SpecialBlocks) {
2432 unsigned PrevBlockID = PrevBlock->getBlockID();
2433 CFGBlockInfo *PrevBlockInfo = &BlockInfo[PrevBlockID];
2435 if (!LocksetInitialized) {
2436 CurrBlockInfo->EntrySet = PrevBlockInfo->ExitSet;
2437 LocksetInitialized =
true;
2444 const Stmt *Terminator = PrevBlock->getTerminator();
2445 bool IsLoop = Terminator && isa<ContinueStmt>(Terminator);
2447 FactSet PrevLockset;
2448 getEdgeLockset(PrevLockset, PrevBlockInfo->ExitSet,
2449 PrevBlock, CurrBlock);
2452 intersectAndWarn(CurrBlockInfo->EntrySet, PrevLockset,
2453 PrevBlockInfo->ExitLoc,
2460 BuildLockset LocksetBuilder(
this, *CurrBlockInfo);
2463 for (
const auto &BI : *CurrBlock) {
2464 switch (BI.getKind()) {
2467 LocksetBuilder.Visit(CS.
getStmt());
2474 if (!DD->hasAttrs())
2482 LocksetBuilder.handleCall(&DRE, DD);
2489 CurrBlockInfo->ExitSet = LocksetBuilder.FSet;
2496 SE = CurrBlock->succ_end(); SI != SE; ++SI) {
2498 if (*SI ==
nullptr || !VisitedBlocks.
alreadySet(*SI))
2502 CFGBlockInfo *PreLoop = &BlockInfo[FirstLoopBlock->
getBlockID()];
2503 CFGBlockInfo *LoopEnd = &BlockInfo[CurrBlockID];
2504 intersectAndWarn(LoopEnd->ExitSet, PreLoop->EntrySet,
2515 if (!Final->Reachable)
2519 FactSet ExpectedExitSet = Initial->EntrySet;
2525 for (
const auto &Lock : ExclusiveLocksAcquired)
2526 ExpectedExitSet.addLock(FactMan, llvm::make_unique<LockableFactEntry>(
2528 for (
const auto &Lock : SharedLocksAcquired)
2529 ExpectedExitSet.addLock(FactMan, llvm::make_unique<LockableFactEntry>(
2531 for (
const auto &Lock : LocksReleased)
2532 ExpectedExitSet.removeLock(FactMan, Lock);
2535 intersectAndWarn(ExpectedExitSet, Final->ExitSet,
2541 Handler.leaveFunction(CurrentFunction);
2554 ThreadSafetyAnalyzer Analyzer(Handler, *BSet);
2555 Analyzer.runAnalysis(AC);
2569 llvm_unreachable(
"Unknown AccessKind");
const PostOrderCFGView * getSortedGraph() const
Represents a function declaration or definition.
Passing a guarded variable by reference.
bool equals(const CapabilityExpr &other) const
A (possibly-)qualified type.
AdjacentBlocks::const_iterator const_pred_iterator
const Stmt * getStmt() const
const CFG * getGraph() const
ConstStmtVisitor - This class implements a simple visitor for Stmt subclasses.
succ_iterator succ_begin()
TypePropertyCache< Private > Cache
Stmt - This represents one statement.
virtual void handleUnmatchedUnlock(StringRef Kind, Name LockName, SourceLocation Loc)
Warn about unlock function calls that do not have a prior matching lock expression.
QualType getPointeeType() const
If this is a pointer, ObjC object pointer, or block pointer, this returns the respective pointee...
C Language Family Type Representation.
static StringRef ClassifyDiagnostic(const CapabilityAttr *A)
unsigned getBlockID() const
QualType getNonReferenceType() const
If Type is a reference type (e.g., const int&), returns the type that the reference refers to ("const...
Represents a call to a C++ constructor.
SourceLocation getBeginLoc() const LLVM_READONLY
T castAs() const
Convert to the specified CFGElement type, asserting that this CFGElement is of the desired type...
LockKind getLockKindFromAccessKind(AccessKind AK)
Helper function that returns a LockKind required for the given level of access.
Represents a C++ constructor within a class.
std::string toString() const
const CXXDestructorDecl * getDestructorDecl(ASTContext &astContext) const
Exclusive/writer lock of a mutex.
ProtectedOperationKind
This enum distinguishes between different kinds of operations that may need to be protected by locks...
bool isTrivialType(const ASTContext &Context) const
Return true if this is a trivial type per (C++0x [basic.types]p9)
static CXXConstructExpr * Create(const ASTContext &Ctx, QualType Ty, SourceLocation Loc, CXXConstructorDecl *Ctor, bool Elidable, ArrayRef< Expr *> Args, bool HadMultipleCandidates, bool ListInitialization, bool StdInitListInitialization, bool ZeroInitialization, ConstructionKind ConstructKind, SourceRange ParenOrBraceRange)
Create a C++ construction expression.
Represents a variable declaration or definition.
ASTContext & getASTContext() const
const T * getAs() const
Member-template getAs<specific type>'.
Expr * IgnoreImplicit() LLVM_READONLY
IgnoreImplicit - Skip past any implicit AST nodes which might surround this expression.
const Stmt * getTriggerStmt() const
static void warnInvalidLock(ThreadSafetyHandler &Handler, const Expr *MutexExp, const NamedDecl *D, const Expr *DeclExp, StringRef Kind)
Issue a warning about an invalid lock expression.
static bool isAssignmentOp(Opcode Opc)
Defines the clang::Expr interface and subclasses for C++ expressions.
void threadSafetyCleanup(BeforeSet *Cache)
static const ValueDecl * getValueDecl(const Expr *Exp)
Gets the value decl pointer from DeclRefExprs or MemberExprs.
LockKind
This enum distinguishes between different kinds of lock actions.
CFGBlockSide
A side (entry or exit) of a CFG node.
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
const til::SExpr * sexpr() const
static std::string toString(const clang::SanitizerSet &Sanitizers)
Produce a string containing comma-separated names of sanitizers in Sanitizers set.
SourceLocation getBeginLoc() const LLVM_READONLY
AnalysisDeclContext contains the context data for the function or method under analysis.
Represents C++ object destructor implicitly generated for automatic object or temporary bound to cons...
bool isReferenceType() const
const DeclGroupRef getDeclGroup() const
static bool neverReturns(const CFGBlock *B)
ArrayRef< ParmVarDecl * > parameters() const
AdjacentBlocks::const_iterator const_succ_iterator
bool alreadySet(const CFGBlock *Block)
Check if the bit for a CFGBlock has been already set.
virtual void handleInvalidLockExp(StringRef Kind, SourceLocation Loc)
Warn about lock expressions which fail to resolve to lockable objects.
static void dump(llvm::raw_ostream &OS, StringRef FunctionName, ArrayRef< CounterExpression > Expressions, ArrayRef< CounterMappingRegion > Regions)
Forward-declares and imports various common LLVM datatypes that clang wants to use unqualified...
Implements a set of CFGBlocks using a BitVector.
A builtin binary operation expression such as "x + y" or "x <= y".
Expr * IgnoreParenCasts() LLVM_READONLY
IgnoreParenCasts - Ignore parentheses and casts.
const VarDecl * getVarDecl() const
CastExpr - Base class for type casts, including both implicit casts (ImplicitCastExpr) and explicit c...
Shared/reader lock of a mutex.
virtual ~ThreadSafetyHandler()
Passing a pt-guarded variable by reference.
Const iterator for iterating over Stmt * arrays that contain only Expr *.
CXXRecordDecl * getAsCXXRecordDecl() const
Retrieves the CXXRecordDecl that this type refers to, either because the type is a RecordType or beca...
bool init(AnalysisDeclContext &AC)
CXXConstructorDecl * getConstructor() const
Get the constructor that this expression will (ultimately) call.
Handler class for thread safety warnings.
Represents a single basic block in a source-level CFG.
Dereferencing a variable (e.g. p in *p = 5;)
Represent the declaration of a variable (in which case it is an lvalue) a function (in which case it ...
This represents one expression.
Stmt * getTerminatorCondition(bool StripParens=true)
Represents a source-level, intra-procedural CFG that represents the control-flow of a Stmt...
Defines an enumeration for C++ overloaded operators.
FunctionDecl * getDirectCallee()
If the callee is a FunctionDecl, return it. Otherwise return null.
void checkBeforeAfter(const ValueDecl *Vd, const FactSet &FSet, ThreadSafetyAnalyzer &Analyzer, SourceLocation Loc, StringRef CapKind)
Return true if any mutexes in FSet are in the acquired_before set of Vd.
DeclContext * getParent()
getParent - Returns the containing DeclContext.
AccessKind
This enum distinguishes between different ways to access (read or write) a variable.
QualType getRecordType(const RecordDecl *Decl) const
UnaryOperator - This represents the unary-expression's (except sizeof and alignof), the postinc/postdec operators from postfix-expression, and various extensions.
Making a function call (e.g. fool())
SourceLocation getEndLoc() const LLVM_READONLY
virtual void printName(raw_ostream &os) const
CFGTerminator getTerminator()
Reading or writing a variable (e.g. x in x = 5;)
std::pair< llvm::NoneType, bool > insert(const CFGBlock *Block)
Set the bit associated with a particular CFGBlock.
BeforeInfo * insertAttrExprs(const ValueDecl *Vd, ThreadSafetyAnalyzer &Analyzer)
Process acquired_before and acquired_after attributes on Vd.
Encodes a location in the source.
Expr * getSubExpr() const
CastKind getCastKind() const
std::string getNameAsString() const
Get a human-readable name for the declaration, even if it is one of the special kinds of names (C++ c...
ASTContext & getASTContext() const LLVM_READONLY
DeclStmt - Adaptor class for mixing declarations with statements and expressions. ...
Represents a static or instance method of a struct/union/class.
SourceLocation getLocation() const
unsigned getNumBlockIDs() const
Returns the total number of BlockIDs allocated (which start at 0).
static bool getStaticBooleanValue(Expr *E, bool &TCond)
SourceLocation getExprLoc() const LLVM_READONLY
getExprLoc - Return the preferred location for the arrow when diagnosing a problem with a generic exp...
Optional< T > getAs() const
Convert to the specified CFGElement type, returning None if this CFGElement is not of the desired typ...
bool shouldIgnore() const
Defines various enumerations that describe declaration and type specifiers.
pred_iterator pred_begin()
static CXXConstructorDecl * findConstructorForByValueReturn(const CXXRecordDecl *RD)
Dataflow Directional Tag Classes.
bool isValid() const
Return true if this is a valid SourceLocation object.
BeforeInfo * getBeforeInfoForDecl(const ValueDecl *Vd, ThreadSafetyAnalyzer &Analyzer)
void runThreadSafetyAnalysis(AnalysisDeclContext &AC, ThreadSafetyHandler &Handler, BeforeSet **Bset)
Check a function's CFG for thread-safety violations.
bool isCopyConstructor(unsigned &TypeQuals) const
Whether this constructor is a copy constructor (C++ [class.copy]p2, which can be used to copy the cla...
const Expr * getInit() const
const CXXRecordDecl * getParent() const
Returns the parent of this method declaration, which is the class in which this method is defined...
std::string toString(const til::SExpr *E)
A helper class that allows the use of isa/cast/dyncast to detect TagType objects of structs/unions/cl...
void dump() const
Dumps the specified AST fragment and all subtrees to llvm::errs().
static Expr * buildFakeCtorCall(CXXConstructorDecl *CD, ArrayRef< Expr *> Args, SourceLocation Loc)
Expr * getArg(unsigned Arg)
Return the specified argument.
const NamedDecl * getDecl() const
llvm::iterator_range< specific_attr_iterator< T > > specific_attrs() const
bool hasNoReturnElement() const
Defines the C++ Decl subclasses, other than those for templates (found in DeclTemplate.h) and friends (in DeclFriend.h).
MemberExpr - [C99 6.5.2.3] Structure and Union Members.
virtual void handleDoubleLock(StringRef Kind, Name LockName, SourceLocation Loc)
Warn about lock function calls for locks which are already held.
Defines the clang::SourceLocation class and associated facilities.
Represents a C++ struct/union/class.
Represents a top-level expression in a basic block.
unsigned kind
All of the diagnostics that can be emitted by the frontend.
virtual void handleMutexHeldEndOfScope(StringRef Kind, Name LockName, SourceLocation LocLocked, SourceLocation LocEndOfScope, LockErrorKind LEK)
Warn about situations where a mutex is sometimes held and sometimes not.
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
StringRef getName() const
Get the name of identifier for this declaration as a StringRef.
A reference to a declared variable, function, enum, etc.
const ValueDecl * valueDecl() const
bool isPointerType() const
bool matches(const til::SExpr *E1, const til::SExpr *E2)
An l-value expression is a reference to an object with independent storage.
A trivial tuple used to represent a source range.
This represents a decl that may have a name.
static void findBlockLocations(CFG *CFGraph, const PostOrderCFGView *SortedGraph, std::vector< CFGBlockInfo > &BlockInfo)
Find the appropriate source locations to use when producing diagnostics for each block in the CFG...
attr::Kind getKind() const
Defines enum values for all the target-independent builtin functions.
Attr - This represents one attribute.
SourceLocation getLocation() const
Expr * IgnoreParens() LLVM_READONLY
IgnoreParens - Ignore parentheses.
Can be either Shared or Exclusive.