40#include "llvm/ADT/ArrayRef.h"
41#include "llvm/ADT/DenseMap.h"
42#include "llvm/ADT/ImmutableMap.h"
43#include "llvm/ADT/STLExtras.h"
44#include "llvm/ADT/SmallVector.h"
45#include "llvm/ADT/StringRef.h"
46#include "llvm/Support/Allocator.h"
47#include "llvm/Support/Casting.h"
48#include "llvm/Support/ErrorHandling.h"
49#include "llvm/Support/raw_ostream.h"
62using namespace threadSafety;
70 const Expr *DeclExp, StringRef Kind) {
84class CapExprSet :
public SmallVector<CapabilityExpr, 4> {
119 SourceKind Source : 8;
128 virtual ~FactEntry() =
default;
133 bool asserted()
const {
return Source == Asserted; }
134 bool declared()
const {
return Source == Declared; }
135 bool managed()
const {
return Source == Managed; }
138 handleRemovalFromIntersection(
const FactSet &FSet, FactManager &FactMan,
141 virtual void handleLock(FactSet &FSet, FactManager &FactMan,
142 const FactEntry &entry,
144 virtual void handleUnlock(FactSet &FSet, FactManager &FactMan,
155using FactID =
unsigned short;
161 std::vector<std::unique_ptr<const FactEntry>> Facts;
164 FactID newFact(std::unique_ptr<FactEntry> Entry) {
165 Facts.push_back(std::move(Entry));
166 return static_cast<unsigned short>(Facts.size() - 1);
169 const FactEntry &operator[](FactID F)
const {
return *Facts[F]; }
186 using iterator = FactVec::iterator;
187 using const_iterator = FactVec::const_iterator;
189 iterator begin() {
return FactIDs.begin(); }
190 const_iterator begin()
const {
return FactIDs.begin(); }
192 iterator end() {
return FactIDs.end(); }
193 const_iterator end()
const {
return FactIDs.end(); }
195 bool isEmpty()
const {
return FactIDs.size() == 0; }
198 bool isEmpty(FactManager &FactMan)
const {
199 for (
const auto FID : *
this) {
200 if (!FactMan[FID].negative())
206 void addLockByID(FactID ID) { FactIDs.push_back(ID); }
208 FactID addLock(FactManager &FM, std::unique_ptr<FactEntry> Entry) {
209 FactID F = FM.newFact(std::move(Entry));
210 FactIDs.push_back(F);
215 unsigned n = FactIDs.size();
219 for (
unsigned i = 0; i < n-1; ++i) {
220 if (FM[FactIDs[i]].
matches(CapE)) {
221 FactIDs[i] = FactIDs[n-1];
226 if (FM[FactIDs[n-1]].
matches(CapE)) {
233 iterator findLockIter(FactManager &FM,
const CapabilityExpr &CapE) {
234 return std::find_if(begin(), end(), [&](FactID ID) {
235 return FM[
ID].matches(CapE);
239 const FactEntry *findLock(FactManager &FM,
const CapabilityExpr &CapE)
const {
240 auto I = std::find_if(begin(), end(), [&](FactID ID) {
241 return FM[
ID].matches(CapE);
243 return I != end() ? &FM[*I] :
nullptr;
246 const FactEntry *findLockUniv(FactManager &FM,
248 auto I = std::find_if(begin(), end(), [&](FactID ID) ->
bool {
249 return FM[
ID].matchesUniv(CapE);
251 return I != end() ? &FM[*I] :
nullptr;
254 const FactEntry *findPartialMatch(FactManager &FM,
256 auto I = std::find_if(begin(), end(), [&](FactID ID) ->
bool {
257 return FM[
ID].partiallyMatches(CapE);
259 return I != end() ? &FM[*I] :
nullptr;
262 bool containsMutexDecl(FactManager &FM,
const ValueDecl* Vd)
const {
263 auto I = std::find_if(begin(), end(), [&](FactID ID) ->
bool {
264 return FM[
ID].valueDecl() == Vd;
270class ThreadSafetyAnalyzer;
275namespace threadSafety {
285 BeforeInfo() =
default;
286 BeforeInfo(BeforeInfo &&) =
default;
290 llvm::DenseMap<const ValueDecl *, std::unique_ptr<BeforeInfo>>;
291 using CycleMap = llvm::DenseMap<const ValueDecl *, bool>;
297 ThreadSafetyAnalyzer& Analyzer);
300 ThreadSafetyAnalyzer &Analyzer);
304 ThreadSafetyAnalyzer& Analyzer,
317class LocalVariableMap;
319using LocalVarContext = llvm::ImmutableMap<const NamedDecl *, unsigned>;
322enum CFGBlockSide { CBS_Entry, CBS_Exit };
335 LocalVarContext EntryContext;
338 LocalVarContext ExitContext;
350 bool Reachable =
false;
352 const FactSet &getSet(CFGBlockSide Side)
const {
353 return Side == CBS_Entry ? EntrySet : ExitSet;
357 return Side == CBS_Entry ? EntryLoc : ExitLoc;
361 CFGBlockInfo(LocalVarContext EmptyCtx)
362 : EntryContext(EmptyCtx), ExitContext(EmptyCtx) {}
365 static CFGBlockInfo getEmptyBlockInfo(LocalVariableMap &M);
381class LocalVariableMap {
383 using Context = LocalVarContext;
389 struct VarDefinition {
391 friend class LocalVariableMap;
397 const Expr *Exp =
nullptr;
405 bool isReference()
const {
return !Exp; }
410 :
Dec(
D), Exp(
E), Ctx(
C) {}
413 VarDefinition(
const NamedDecl *
D,
unsigned R, Context
C)
414 :
Dec(
D), Ref(R), Ctx(
C) {}
418 Context::Factory ContextFactory;
419 std::vector<VarDefinition> VarDefinitions;
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";
477 llvm::errs() <<
"<<NULL>>";
480 Dec->printName(llvm::errs());
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) {
504 D->printName(llvm::errs());
505 llvm::errs() <<
" -> ";
506 dumpVarDefinitionName(I.getData());
507 llvm::errs() <<
"\n";
513 std::vector<CFGBlockInfo> &BlockInfo);
516 friend class VarMapBuilder;
519 unsigned getContextIndex() {
return SavedContexts.size()-1; }
522 void saveContext(
const Stmt *S, Context
C) {
523 SavedContexts.push_back(std::make_pair(S,
C));
528 Context addDefinition(
const NamedDecl *
D,
const Expr *Exp, Context Ctx) {
529 assert(!Ctx.contains(
D));
530 unsigned newID = VarDefinitions.size();
531 Context NewCtx = ContextFactory.add(Ctx,
D, newID);
532 VarDefinitions.push_back(VarDefinition(
D, Exp, Ctx));
537 Context addReference(
const NamedDecl *
D,
unsigned i, Context Ctx) {
538 unsigned newID = VarDefinitions.size();
539 Context NewCtx = ContextFactory.add(Ctx,
D, newID);
540 VarDefinitions.push_back(VarDefinition(
D, i, Ctx));
546 Context updateDefinition(
const NamedDecl *
D,
Expr *Exp, Context Ctx) {
547 if (Ctx.contains(
D)) {
548 unsigned newID = VarDefinitions.size();
549 Context NewCtx = ContextFactory.remove(Ctx,
D);
550 NewCtx = ContextFactory.add(NewCtx,
D, newID);
551 VarDefinitions.push_back(VarDefinition(
D, Exp, Ctx));
559 Context clearDefinition(
const NamedDecl *
D, Context Ctx) {
560 Context NewCtx = Ctx;
561 if (NewCtx.contains(
D)) {
562 NewCtx = ContextFactory.remove(NewCtx,
D);
563 NewCtx = ContextFactory.add(NewCtx,
D, 0);
569 Context removeDefinition(
const NamedDecl *
D, Context Ctx) {
570 Context NewCtx = Ctx;
571 if (NewCtx.contains(
D)) {
572 NewCtx = ContextFactory.remove(NewCtx,
D);
577 Context intersectContexts(Context C1, Context C2);
578 Context createReferenceContext(Context
C);
579 void intersectBackEdge(Context C1, Context C2);
585CFGBlockInfo CFGBlockInfo::getEmptyBlockInfo(LocalVariableMap &M) {
586 return CFGBlockInfo(M.getEmptyContext());
594 LocalVariableMap* VMap;
595 LocalVariableMap::Context Ctx;
597 VarMapBuilder(LocalVariableMap *VM, LocalVariableMap::Context
C)
598 : VMap(VM), Ctx(
C) {}
600 void VisitDeclStmt(
const DeclStmt *S);
607void VarMapBuilder::VisitDeclStmt(
const DeclStmt *S) {
608 bool modifiedCtx =
false;
610 for (
const auto *
D : DGrp) {
611 if (
const auto *VD = dyn_cast_or_null<VarDecl>(
D)) {
612 const Expr *
E = VD->getInit();
616 if (
T.isTrivialType(VD->getASTContext())) {
617 Ctx = VMap->addDefinition(VD,
E, Ctx);
623 VMap->saveContext(S, Ctx);
627void VarMapBuilder::VisitBinaryOperator(
const BinaryOperator *BO) {
634 if (
const auto *DRE = dyn_cast<DeclRefExpr>(LHSExp)) {
636 if (Ctx.lookup(VDec)) {
638 Ctx = VMap->updateDefinition(VDec, BO->
getRHS(), Ctx);
641 Ctx = VMap->clearDefinition(VDec, Ctx);
642 VMap->saveContext(BO, Ctx);
650LocalVariableMap::Context
651LocalVariableMap::intersectContexts(Context C1, Context C2) {
653 for (
const auto &
P : C1) {
655 const unsigned *i2 = C2.lookup(Dec);
657 Result = removeDefinition(Dec, Result);
658 else if (*i2 !=
P.second)
659 Result = clearDefinition(Dec, Result);
667LocalVariableMap::Context LocalVariableMap::createReferenceContext(Context
C) {
668 Context Result = getEmptyContext();
669 for (
const auto &
P :
C)
670 Result = addReference(
P.first,
P.second, Result);
677void LocalVariableMap::intersectBackEdge(Context C1, Context C2) {
678 for (
const auto &
P : C1) {
679 unsigned i1 =
P.second;
680 VarDefinition *VDef = &VarDefinitions[i1];
681 assert(VDef->isReference());
683 const unsigned *i2 = C2.lookup(
P.first);
684 if (!i2 || (*i2 != i1))
726void LocalVariableMap::traverseCFG(
CFG *CFGraph,
728 std::vector<CFGBlockInfo> &BlockInfo) {
731 for (
const auto *CurrBlock : *SortedGraph) {
732 unsigned CurrBlockID = CurrBlock->getBlockID();
733 CFGBlockInfo *CurrBlockInfo = &BlockInfo[CurrBlockID];
735 VisitedBlocks.insert(CurrBlock);
738 bool HasBackEdges =
false;
741 PE = CurrBlock->pred_end(); PI != PE; ++PI) {
743 if (*PI ==
nullptr || !VisitedBlocks.alreadySet(*PI)) {
748 unsigned PrevBlockID = (*PI)->getBlockID();
749 CFGBlockInfo *PrevBlockInfo = &BlockInfo[PrevBlockID];
752 CurrBlockInfo->EntryContext = PrevBlockInfo->ExitContext;
756 CurrBlockInfo->EntryContext =
757 intersectContexts(CurrBlockInfo->EntryContext,
758 PrevBlockInfo->ExitContext);
765 CurrBlockInfo->EntryContext =
766 createReferenceContext(CurrBlockInfo->EntryContext);
769 saveContext(
nullptr, CurrBlockInfo->EntryContext);
770 CurrBlockInfo->EntryIndex = getContextIndex();
773 VarMapBuilder VMapBuilder(
this, CurrBlockInfo->EntryContext);
774 for (
const auto &BI : *CurrBlock) {
775 switch (BI.getKind()) {
778 VMapBuilder.Visit(CS.
getStmt());
785 CurrBlockInfo->ExitContext = VMapBuilder.Ctx;
789 SE = CurrBlock->succ_end(); SI != SE; ++SI) {
791 if (*SI ==
nullptr || !VisitedBlocks.alreadySet(*SI))
795 Context LoopBegin = BlockInfo[FirstLoopBlock->
getBlockID()].EntryContext;
796 Context LoopEnd = CurrBlockInfo->ExitContext;
797 intersectBackEdge(LoopBegin, LoopEnd);
803 saveContext(
nullptr, BlockInfo[exitID].ExitContext);
810 std::vector<CFGBlockInfo> &BlockInfo) {
811 for (
const auto *CurrBlock : *SortedGraph) {
812 CFGBlockInfo *CurrBlockInfo = &BlockInfo[CurrBlock->getBlockID()];
816 if (
const Stmt *S = CurrBlock->getTerminatorStmt()) {
817 CurrBlockInfo->EntryLoc = CurrBlockInfo->ExitLoc = S->getBeginLoc();
820 BE = CurrBlock->rend(); BI != BE; ++BI) {
822 if (std::optional<CFGStmt> CS = BI->getAs<
CFGStmt>()) {
823 CurrBlockInfo->ExitLoc = CS->getStmt()->getBeginLoc();
829 if (CurrBlockInfo->ExitLoc.isValid()) {
832 for (
const auto &BI : *CurrBlock) {
834 if (std::optional<CFGStmt> CS = BI.getAs<
CFGStmt>()) {
835 CurrBlockInfo->EntryLoc = CS->getStmt()->getBeginLoc();
839 }
else if (CurrBlock->pred_size() == 1 && *CurrBlock->pred_begin() &&
840 CurrBlock != &CFGraph->
getExit()) {
843 CurrBlockInfo->EntryLoc = CurrBlockInfo->ExitLoc =
844 BlockInfo[(*CurrBlock->pred_begin())->getBlockID()].ExitLoc;
845 }
else if (CurrBlock->succ_size() == 1 && *CurrBlock->succ_begin()) {
848 CurrBlockInfo->EntryLoc = CurrBlockInfo->ExitLoc =
849 BlockInfo[(*CurrBlock->succ_begin())->getBlockID()].EntryLoc;
856class LockableFactEntry :
public FactEntry {
859 SourceKind Src = Acquired)
860 : FactEntry(CE, LK,
Loc, Src) {}
863 handleRemovalFromIntersection(
const FactSet &FSet, FactManager &FactMan,
866 if (!asserted() && !negative() && !isUniversal()) {
872 void handleLock(FactSet &FSet, FactManager &FactMan,
const FactEntry &entry,
878 void handleUnlock(FactSet &FSet, FactManager &FactMan,
882 FSet.removeLock(FactMan, Cp);
884 FSet.addLock(FactMan, std::make_unique<LockableFactEntry>(
890class ScopedLockableFactEntry :
public FactEntry {
892 enum UnderlyingCapabilityKind {
895 UCK_ReleasedExclusive,
898 struct UnderlyingCapability {
900 UnderlyingCapabilityKind
Kind;
910 UnderlyingMutexes.push_back(UnderlyingCapability{M, UCK_Acquired});
914 UnderlyingMutexes.push_back(UnderlyingCapability{M, UCK_ReleasedExclusive});
918 UnderlyingMutexes.push_back(UnderlyingCapability{M, UCK_ReleasedShared});
922 handleRemovalFromIntersection(
const FactSet &FSet, FactManager &FactMan,
925 for (
const auto &UnderlyingMutex : UnderlyingMutexes) {
926 const auto *Entry = FSet.findLock(FactMan, UnderlyingMutex.Cap);
927 if ((UnderlyingMutex.Kind == UCK_Acquired && Entry) ||
928 (UnderlyingMutex.Kind != UCK_Acquired && !Entry)) {
932 UnderlyingMutex.Cap.toString(), loc(),
938 void handleLock(FactSet &FSet, FactManager &FactMan,
const FactEntry &entry,
940 for (
const auto &UnderlyingMutex : UnderlyingMutexes) {
941 if (UnderlyingMutex.Kind == UCK_Acquired)
942 lock(FSet, FactMan, UnderlyingMutex.Cap, entry.kind(), entry.loc(),
945 unlock(FSet, FactMan, UnderlyingMutex.Cap, entry.loc(), &Handler);
949 void handleUnlock(FactSet &FSet, FactManager &FactMan,
953 assert(!Cp.
negative() &&
"Managing object cannot be negative.");
954 for (
const auto &UnderlyingMutex : UnderlyingMutexes) {
958 if (UnderlyingMutex.Kind == UCK_Acquired) {
959 unlock(FSet, FactMan, UnderlyingMutex.Cap, UnlockLoc, TSHandler);
961 LockKind kind = UnderlyingMutex.Kind == UCK_ReleasedShared
964 lock(FSet, FactMan, UnderlyingMutex.Cap, kind, UnlockLoc, TSHandler);
968 FSet.removeLock(FactMan, Cp);
972 void lock(FactSet &FSet, FactManager &FactMan,
const CapabilityExpr &Cp,
975 if (
const FactEntry *Fact = FSet.findLock(FactMan, Cp)) {
980 FSet.removeLock(FactMan, !Cp);
981 FSet.addLock(FactMan,
982 std::make_unique<LockableFactEntry>(Cp, kind, loc, Managed));
986 void unlock(FactSet &FSet, FactManager &FactMan,
const CapabilityExpr &Cp,
988 if (FSet.findLock(FactMan, Cp)) {
989 FSet.removeLock(FactMan, Cp);
990 FSet.addLock(FactMan, std::make_unique<LockableFactEntry>(
992 }
else if (Handler) {
994 if (
const FactEntry *Neg = FSet.findLock(FactMan, !Cp))
995 PrevLoc =
Neg->loc();
1002class ThreadSafetyAnalyzer {
1003 friend class BuildLockset;
1006 llvm::BumpPtrAllocator Bpa;
1012 LocalVariableMap LocalVarMap;
1014 llvm::SmallDenseMap<const Expr *, til::LiteralPtr *> ConstructedObjects;
1015 FactManager FactMan;
1016 std::vector<CFGBlockInfo> BlockInfo;
1022 : Arena(&Bpa), SxBuilder(Arena), Handler(H), GlobalBeforeSet(Bset) {}
1026 void addLock(FactSet &FSet, std::unique_ptr<FactEntry> Entry,
1027 bool ReqAttr =
false);
1031 template <
typename AttrType>
1032 void getMutexIDs(CapExprSet &Mtxs, AttrType *
Attr,
const Expr *Exp,
1035 template <
class AttrType>
1036 void getMutexIDs(CapExprSet &Mtxs, AttrType *
Attr,
const Expr *Exp,
1039 Expr *BrE,
bool Neg);
1041 const CallExpr* getTrylockCallExpr(
const Stmt *Cond, LocalVarContext
C,
1044 void getEdgeLockset(FactSet &Result,
const FactSet &ExitSet,
1048 bool join(
const FactEntry &a,
const FactEntry &
b,
bool CanModify);
1050 void intersectAndWarn(FactSet &EntrySet,
const FactSet &ExitSet,
1054 void intersectAndWarn(FactSet &EntrySet,
const FactSet &ExitSet,
1056 intersectAndWarn(EntrySet, ExitSet, JoinLoc, LEK, LEK);
1061 void warnIfMutexNotHeld(
const FactSet &FSet,
const NamedDecl *
D,
1065 void warnIfMutexHeld(
const FactSet &FSet,
const NamedDecl *
D,
const Expr *Exp,
1069 void checkAccess(
const FactSet &FSet,
const Expr *Exp,
AccessKind AK,
1071 void checkPtAccess(
const FactSet &FSet,
const Expr *Exp,
AccessKind AK,
1079 ThreadSafetyAnalyzer& Analyzer) {
1081 BeforeInfo *Info =
nullptr;
1085 std::unique_ptr<BeforeInfo> &InfoPtr = BMap[Vd];
1087 InfoPtr.reset(
new BeforeInfo());
1088 Info = InfoPtr.get();
1091 for (
const auto *At : Vd->
attrs()) {
1092 switch (At->getKind()) {
1093 case attr::AcquiredBefore: {
1094 const auto *A = cast<AcquiredBeforeAttr>(At);
1097 for (
const auto *Arg : A->args()) {
1099 Analyzer.SxBuilder.translateAttrExpr(Arg,
nullptr);
1101 Info->Vect.push_back(Cpvd);
1102 const auto It = BMap.find(Cpvd);
1103 if (It == BMap.end())
1109 case attr::AcquiredAfter: {
1110 const auto *A = cast<AcquiredAfterAttr>(At);
1113 for (
const auto *Arg : A->args()) {
1115 Analyzer.SxBuilder.translateAttrExpr(Arg,
nullptr);
1119 ArgInfo->Vect.push_back(Vd);
1132BeforeSet::BeforeInfo *
1134 ThreadSafetyAnalyzer &Analyzer) {
1135 auto It = BMap.find(Vd);
1136 BeforeInfo *Info =
nullptr;
1137 if (It == BMap.end())
1140 Info = It->second.get();
1141 assert(Info &&
"BMap contained nullptr?");
1147 const FactSet& FSet,
1148 ThreadSafetyAnalyzer& Analyzer,
1160 if (Info->Visited == 1)
1163 if (Info->Visited == 2)
1166 if (Info->Vect.empty())
1169 InfoVect.push_back(Info);
1171 for (
const auto *Vdb : Info->Vect) {
1173 if (FSet.containsMutexDecl(Analyzer.FactMan, Vdb)) {
1174 StringRef L1 = StartVd->
getName();
1175 StringRef L2 = Vdb->getName();
1176 Analyzer.Handler.handleLockAcquiredBefore(CapKind, L1, L2,
Loc);
1180 if (!CycMap.contains(Vd)) {
1181 CycMap.insert(std::make_pair(Vd,
true));
1183 Analyzer.Handler.handleBeforeAfterCycle(L1, Vd->
getLocation());
1193 for (
auto *Info : InfoVect)
1199 if (
const auto *CE = dyn_cast<ImplicitCastExpr>(Exp))
1202 if (
const auto *DR = dyn_cast<DeclRefExpr>(Exp))
1203 return DR->getDecl();
1205 if (
const auto *ME = dyn_cast<MemberExpr>(Exp))
1206 return ME->getMemberDecl();
1213template <
typename Ty>
1214class has_arg_iterator_range {
1215 using yes =
char[1];
1218 template <
typename Inner>
1219 static yes& test(Inner *I,
decltype(I->args()) * =
nullptr);
1222 static no& test(...);
1225 static const bool value =
sizeof(test<Ty>(
nullptr)) ==
sizeof(yes);
1230bool ThreadSafetyAnalyzer::inCurrentScope(
const CapabilityExpr &CapE) {
1232 assert(SExp &&
"Null expressions should be ignored");
1234 if (
const auto *LP = dyn_cast<til::LiteralPtr>(SExp)) {
1247 if (
const auto *
P = dyn_cast<til::Project>(SExp)) {
1248 if (!isa_and_nonnull<CXXMethodDecl>(CurrentFunction))
1259void ThreadSafetyAnalyzer::addLock(FactSet &FSet,
1260 std::unique_ptr<FactEntry> Entry,
1262 if (Entry->shouldIgnore())
1265 if (!ReqAttr && !Entry->negative()) {
1268 const FactEntry *Nen = FSet.findLock(FactMan, NegC);
1270 FSet.removeLock(FactMan, NegC);
1273 if (inCurrentScope(*Entry) && !Entry->asserted())
1281 !Entry->asserted() && !Entry->declared()) {
1282 GlobalBeforeSet->checkBeforeAfter(Entry->valueDecl(), FSet, *
this,
1283 Entry->loc(), Entry->getKind());
1287 if (
const FactEntry *Cp = FSet.findLock(FactMan, *Entry)) {
1288 if (!Entry->asserted())
1289 Cp->handleLock(FSet, FactMan, *Entry, Handler);
1291 FSet.addLock(FactMan, std::move(Entry));
1297void ThreadSafetyAnalyzer::removeLock(FactSet &FSet,
const CapabilityExpr &Cp,
1299 bool FullyRemove,
LockKind ReceivedKind) {
1303 const FactEntry *LDat = FSet.findLock(FactMan, Cp);
1306 if (
const FactEntry *Neg = FSet.findLock(FactMan, !Cp))
1307 PrevLoc =
Neg->loc();
1315 if (ReceivedKind !=
LK_Generic && LDat->kind() != ReceivedKind) {
1317 ReceivedKind, LDat->loc(), UnlockLoc);
1320 LDat->handleUnlock(FSet, FactMan, Cp, UnlockLoc, FullyRemove, Handler);
1325template <
typename AttrType>
1326void ThreadSafetyAnalyzer::getMutexIDs(CapExprSet &Mtxs, AttrType *
Attr,
1329 if (
Attr->args_size() == 0) {
1338 Mtxs.push_back_nodup(Cp);
1342 for (
const auto *Arg :
Attr->args()) {
1350 Mtxs.push_back_nodup(Cp);
1357template <
class AttrType>
1358void ThreadSafetyAnalyzer::getMutexIDs(CapExprSet &Mtxs, AttrType *
Attr,
1362 Expr *BrE,
bool Neg) {
1364 bool branch =
false;
1365 if (
const auto *BLE = dyn_cast_or_null<CXXBoolLiteralExpr>(BrE))
1366 branch = BLE->getValue();
1367 else if (
const auto *ILE = dyn_cast_or_null<IntegerLiteral>(BrE))
1368 branch = ILE->getValue().getBoolValue();
1370 int branchnum = branch ? 0 : 1;
1372 branchnum = !branchnum;
1377 SE = PredBlock->
succ_end(); SI != SE && i < 2; ++SI, ++i) {
1378 if (*SI == CurrBlock && i == branchnum)
1379 getMutexIDs(Mtxs,
Attr, Exp,
D);
1384 if (isa<CXXNullPtrLiteralExpr>(
E) || isa<GNUNullExpr>(
E)) {
1387 }
else if (
const auto *BLE = dyn_cast<CXXBoolLiteralExpr>(
E)) {
1388 TCond = BLE->getValue();
1390 }
else if (
const auto *ILE = dyn_cast<IntegerLiteral>(
E)) {
1391 TCond = ILE->getValue().getBoolValue();
1393 }
else if (
auto *CE = dyn_cast<ImplicitCastExpr>(
E))
1401const CallExpr* ThreadSafetyAnalyzer::getTrylockCallExpr(
const Stmt *Cond,
1407 if (
const auto *CallExp = dyn_cast<CallExpr>(Cond)) {
1408 if (CallExp->getBuiltinCallee() == Builtin::BI__builtin_expect)
1409 return getTrylockCallExpr(CallExp->getArg(0),
C, Negate);
1412 else if (
const auto *PE = dyn_cast<ParenExpr>(Cond))
1413 return getTrylockCallExpr(PE->getSubExpr(),
C, Negate);
1414 else if (
const auto *CE = dyn_cast<ImplicitCastExpr>(Cond))
1415 return getTrylockCallExpr(CE->getSubExpr(),
C, Negate);
1416 else if (
const auto *FE = dyn_cast<FullExpr>(Cond))
1417 return getTrylockCallExpr(FE->getSubExpr(),
C, Negate);
1418 else if (
const auto *DRE = dyn_cast<DeclRefExpr>(Cond)) {
1419 const Expr *
E = LocalVarMap.lookupExpr(DRE->getDecl(),
C);
1420 return getTrylockCallExpr(
E,
C, Negate);
1422 else if (
const auto *UOP = dyn_cast<UnaryOperator>(Cond)) {
1423 if (UOP->getOpcode() == UO_LNot) {
1425 return getTrylockCallExpr(UOP->getSubExpr(),
C, Negate);
1429 else if (
const auto *BOP = dyn_cast<BinaryOperator>(Cond)) {
1430 if (BOP->getOpcode() == BO_EQ || BOP->getOpcode() == BO_NE) {
1431 if (BOP->getOpcode() == BO_NE)
1436 if (!TCond) Negate = !Negate;
1437 return getTrylockCallExpr(BOP->getLHS(),
C, Negate);
1441 if (!TCond) Negate = !Negate;
1442 return getTrylockCallExpr(BOP->getRHS(),
C, Negate);
1446 if (BOP->getOpcode() == BO_LAnd) {
1448 return getTrylockCallExpr(BOP->getRHS(),
C, Negate);
1450 if (BOP->getOpcode() == BO_LOr)
1451 return getTrylockCallExpr(BOP->getRHS(),
C, Negate);
1453 }
else if (
const auto *COP = dyn_cast<ConditionalOperator>(Cond)) {
1457 if (TCond && !FCond)
1458 return getTrylockCallExpr(COP->getCond(),
C, Negate);
1459 if (!TCond && FCond) {
1461 return getTrylockCallExpr(COP->getCond(),
C, Negate);
1471void ThreadSafetyAnalyzer::getEdgeLockset(FactSet&
Result,
1472 const FactSet &ExitSet,
1482 bool Negate =
false;
1483 const CFGBlockInfo *PredBlockInfo = &BlockInfo[PredBlock->
getBlockID()];
1484 const LocalVarContext &LVarCtx = PredBlockInfo->ExitContext;
1486 const auto *Exp = getTrylockCallExpr(Cond, LVarCtx, Negate);
1490 auto *FunDecl = dyn_cast_or_null<NamedDecl>(Exp->getCalleeDecl());
1491 if(!FunDecl || !FunDecl->hasAttrs())
1494 CapExprSet ExclusiveLocksToAdd;
1495 CapExprSet SharedLocksToAdd;
1498 for (
const auto *
Attr : FunDecl->attrs()) {
1500 case attr::TryAcquireCapability: {
1501 auto *A = cast<TryAcquireCapabilityAttr>(
Attr);
1502 getMutexIDs(A->isShared() ? SharedLocksToAdd : ExclusiveLocksToAdd, A,
1503 Exp, FunDecl, PredBlock, CurrBlock, A->getSuccessValue(),
1507 case attr::ExclusiveTrylockFunction: {
1508 const auto *A = cast<ExclusiveTrylockFunctionAttr>(
Attr);
1509 getMutexIDs(ExclusiveLocksToAdd, A, Exp, FunDecl, PredBlock, CurrBlock,
1510 A->getSuccessValue(), Negate);
1513 case attr::SharedTrylockFunction: {
1514 const auto *A = cast<SharedTrylockFunctionAttr>(
Attr);
1515 getMutexIDs(SharedLocksToAdd, A, Exp, FunDecl, PredBlock, CurrBlock,
1516 A->getSuccessValue(), Negate);
1526 for (
const auto &ExclusiveLockToAdd : ExclusiveLocksToAdd)
1527 addLock(
Result, std::make_unique<LockableFactEntry>(ExclusiveLockToAdd,
1529 for (
const auto &SharedLockToAdd : SharedLocksToAdd)
1530 addLock(
Result, std::make_unique<LockableFactEntry>(SharedLockToAdd,
1542 friend class ThreadSafetyAnalyzer;
1544 ThreadSafetyAnalyzer *Analyzer;
1547 const FactSet &FunctionExitFSet;
1548 LocalVariableMap::Context LVarCtx;
1555 Analyzer->checkAccess(FSet, Exp, AK, POK);
1559 Analyzer->checkPtAccess(FSet, Exp, AK, POK);
1568 bool SkipFirstParam =
false);
1571 BuildLockset(ThreadSafetyAnalyzer *Anlzr, CFGBlockInfo &Info,
1572 const FactSet &FunctionExitFSet)
1574 FunctionExitFSet(FunctionExitFSet), LVarCtx(Info.EntryContext),
1575 CtxIndex(Info.EntryIndex) {}
1579 void VisitCastExpr(
const CastExpr *CE);
1580 void VisitCallExpr(
const CallExpr *Exp);
1582 void VisitDeclStmt(
const DeclStmt *S);
1591void ThreadSafetyAnalyzer::warnIfMutexNotHeld(
1606 const FactEntry *LDat = FSet.findLock(FactMan, !Cp);
1609 (!Cp).toString(),
Loc);
1615 if (!inCurrentScope(Cp))
1619 LDat = FSet.findLock(FactMan, Cp);
1626 const FactEntry *LDat = FSet.findLockUniv(FactMan, Cp);
1627 bool NoError =
true;
1630 LDat = FSet.findPartialMatch(FactMan, Cp);
1633 std::string PartMatchStr = LDat->toString();
1634 StringRef PartMatchName(PartMatchStr);
1644 if (NoError && LDat && !LDat->isAtLeast(LK)) {
1650void ThreadSafetyAnalyzer::warnIfMutexHeld(
const FactSet &FSet,
1663 const FactEntry *LDat = FSet.findLock(FactMan, Cp);
1675void ThreadSafetyAnalyzer::checkAccess(
const FactSet &FSet,
const Expr *Exp,
1684 while (
const auto *DRE = dyn_cast<DeclRefExpr>(Exp)) {
1685 const auto *VD = dyn_cast<VarDecl>(DRE->getDecl()->getCanonicalDecl());
1687 if (
const auto *
E = VD->getInit()) {
1698 if (
const auto *UO = dyn_cast<UnaryOperator>(Exp)) {
1700 if (UO->getOpcode() == UO_Deref)
1701 checkPtAccess(FSet, UO->getSubExpr(), AK, POK);
1705 if (
const auto *BO = dyn_cast<BinaryOperator>(Exp)) {
1708 return checkAccess(FSet, BO->
getLHS(), AK, POK);
1710 return checkPtAccess(FSet, BO->
getLHS(), AK, POK);
1716 if (
const auto *AE = dyn_cast<ArraySubscriptExpr>(Exp)) {
1717 checkPtAccess(FSet, AE->getLHS(), AK, POK);
1721 if (
const auto *ME = dyn_cast<MemberExpr>(Exp)) {
1723 checkPtAccess(FSet, ME->getBase(), AK, POK);
1725 checkAccess(FSet, ME->getBase(), AK, POK);
1732 if (
D->
hasAttr<GuardedVarAttr>() && FSet.isEmpty(FactMan)) {
1737 warnIfMutexNotHeld(FSet,
D, Exp, AK, I->getArg(), POK,
nullptr,
Loc);
1742void ThreadSafetyAnalyzer::checkPtAccess(
const FactSet &FSet,
const Expr *Exp,
1746 if (
const auto *PE = dyn_cast<ParenExpr>(Exp)) {
1747 Exp = PE->getSubExpr();
1750 if (
const auto *CE = dyn_cast<CastExpr>(Exp)) {
1751 if (CE->getCastKind() == CK_ArrayToPointerDecay) {
1754 checkAccess(FSet, CE->getSubExpr(), AK, POK);
1757 Exp = CE->getSubExpr();
1773 if (
D->
hasAttr<PtGuardedVarAttr>() && FSet.isEmpty(FactMan))
1777 warnIfMutexNotHeld(FSet,
D, Exp, AK, I->getArg(), PtPOK,
nullptr,
1796void BuildLockset::handleCall(
const Expr *Exp,
const NamedDecl *
D,
1798 CapExprSet ExclusiveLocksToAdd, SharedLocksToAdd;
1799 CapExprSet ExclusiveLocksToRemove, SharedLocksToRemove, GenericLocksToRemove;
1800 CapExprSet ScopedReqsAndExcludes;
1808 std::pair<til::LiteralPtr *, StringRef> Placeholder =
1809 Analyzer->SxBuilder.createThisPlaceholder(Exp);
1810 [[maybe_unused]]
auto inserted =
1811 Analyzer->ConstructedObjects.insert({Exp, Placeholder.first});
1812 assert(inserted.second &&
"Are we visiting the same expression again?");
1813 if (isa<CXXConstructExpr>(Exp))
1814 Self = Placeholder.first;
1815 if (TagT->getDecl()->hasAttr<ScopedLockableAttr>())
1816 Scp =
CapabilityExpr(Placeholder.first, Placeholder.second,
false);
1824 switch (At->getKind()) {
1827 case attr::AcquireCapability: {
1828 const auto *A = cast<AcquireCapabilityAttr>(At);
1829 Analyzer->getMutexIDs(A->isShared() ? SharedLocksToAdd
1830 : ExclusiveLocksToAdd,
1838 case attr::AssertExclusiveLock: {
1839 const auto *A = cast<AssertExclusiveLockAttr>(At);
1841 CapExprSet AssertLocks;
1842 Analyzer->getMutexIDs(AssertLocks, A, Exp,
D,
Self);
1843 for (
const auto &AssertLock : AssertLocks)
1845 FSet, std::make_unique<LockableFactEntry>(
1849 case attr::AssertSharedLock: {
1850 const auto *A = cast<AssertSharedLockAttr>(At);
1852 CapExprSet AssertLocks;
1853 Analyzer->getMutexIDs(AssertLocks, A, Exp,
D,
Self);
1854 for (
const auto &AssertLock : AssertLocks)
1856 FSet, std::make_unique<LockableFactEntry>(
1861 case attr::AssertCapability: {
1862 const auto *A = cast<AssertCapabilityAttr>(At);
1863 CapExprSet AssertLocks;
1864 Analyzer->getMutexIDs(AssertLocks, A, Exp,
D,
Self);
1865 for (
const auto &AssertLock : AssertLocks)
1866 Analyzer->addLock(FSet, std::make_unique<LockableFactEntry>(
1869 Loc, FactEntry::Asserted));
1875 case attr::ReleaseCapability: {
1876 const auto *A = cast<ReleaseCapabilityAttr>(At);
1878 Analyzer->getMutexIDs(GenericLocksToRemove, A, Exp,
D,
Self);
1879 else if (A->isShared())
1880 Analyzer->getMutexIDs(SharedLocksToRemove, A, Exp,
D,
Self);
1882 Analyzer->getMutexIDs(ExclusiveLocksToRemove, A, Exp,
D,
Self);
1886 case attr::RequiresCapability: {
1887 const auto *A = cast<RequiresCapabilityAttr>(At);
1888 for (
auto *Arg : A->args()) {
1889 Analyzer->warnIfMutexNotHeld(FSet,
D, Exp,
1894 Analyzer->getMutexIDs(ScopedReqsAndExcludes, A, Exp,
D,
Self);
1899 case attr::LocksExcluded: {
1900 const auto *A = cast<LocksExcludedAttr>(At);
1901 for (
auto *Arg : A->args()) {
1902 Analyzer->warnIfMutexHeld(FSet,
D, Exp, Arg,
Self,
Loc);
1905 Analyzer->getMutexIDs(ScopedReqsAndExcludes, A, Exp,
D,
Self);
1918 bool Dtor = isa<CXXDestructorDecl>(
D);
1919 for (
const auto &M : ExclusiveLocksToRemove)
1921 for (
const auto &M : SharedLocksToRemove)
1923 for (
const auto &M : GenericLocksToRemove)
1927 FactEntry::SourceKind Source =
1928 !Scp.
shouldIgnore() ? FactEntry::Managed : FactEntry::Acquired;
1929 for (
const auto &M : ExclusiveLocksToAdd)
1930 Analyzer->addLock(FSet, std::make_unique<LockableFactEntry>(M,
LK_Exclusive,
1932 for (
const auto &M : SharedLocksToAdd)
1934 FSet, std::make_unique<LockableFactEntry>(M,
LK_Shared,
Loc, Source));
1938 auto ScopedEntry = std::make_unique<ScopedLockableFactEntry>(Scp,
Loc);
1939 for (
const auto &M : ExclusiveLocksToAdd)
1940 ScopedEntry->addLock(M);
1941 for (
const auto &M : SharedLocksToAdd)
1942 ScopedEntry->addLock(M);
1943 for (
const auto &M : ScopedReqsAndExcludes)
1944 ScopedEntry->addLock(M);
1945 for (
const auto &M : ExclusiveLocksToRemove)
1946 ScopedEntry->addExclusiveUnlock(M);
1947 for (
const auto &M : SharedLocksToRemove)
1948 ScopedEntry->addSharedUnlock(M);
1949 Analyzer->addLock(FSet, std::move(ScopedEntry));
1956void BuildLockset::VisitUnaryOperator(
const UnaryOperator *UO) {
1972void BuildLockset::VisitBinaryOperator(
const BinaryOperator *BO) {
1977 LVarCtx = Analyzer->LocalVarMap.getNextContext(CtxIndex, BO, LVarCtx);
1985void BuildLockset::VisitCastExpr(
const CastExpr *CE) {
1991void BuildLockset::examineArguments(
const FunctionDecl *FD,
1994 bool SkipFirstParam) {
2004 if (FD->
hasAttr<NoThreadSafetyAnalysisAttr>())
2008 auto Param = Params.begin();
2013 for (
auto Arg = ArgBegin; Param != Params.end() && Arg != ArgEnd;
2021void BuildLockset::VisitCallExpr(
const CallExpr *Exp) {
2022 if (
const auto *CE = dyn_cast<CXXMemberCallExpr>(Exp)) {
2023 const auto *ME = dyn_cast<MemberExpr>(CE->getCallee());
2028 if (ME->isArrow()) {
2030 checkPtAccess(CE->getImplicitObjectArgument(),
AK_Read);
2033 checkAccess(CE->getImplicitObjectArgument(),
AK_Read);
2037 examineArguments(CE->getDirectCallee(), CE->arg_begin(), CE->arg_end());
2038 }
else if (
const auto *OE = dyn_cast<CXXOperatorCallExpr>(Exp)) {
2046 case OO_PercentEqual:
2050 case OO_LessLessEqual:
2051 case OO_GreaterGreaterEqual:
2052 checkAccess(OE->getArg(1),
AK_Read);
2062 if (!(OEop == OO_Star && OE->getNumArgs() > 1)) {
2064 checkPtAccess(OE->getArg(0),
AK_Read);
2069 const Expr *Obj = OE->getArg(0);
2075 examineArguments(FD, std::next(OE->arg_begin()), OE->arg_end(),
2076 !isa<CXXMethodDecl>(FD));
2092 if (
D &&
D->isCopyConstructor()) {
2103 if (
auto *CE = dyn_cast<CastExpr>(
E))
2106 if (
auto *CE = dyn_cast<CastExpr>(
E))
2107 if (CE->
getCastKind() == CK_ConstructorConversion ||
2110 if (
auto *BTE = dyn_cast<CXXBindTemporaryExpr>(
E))
2111 E = BTE->getSubExpr();
2115void BuildLockset::VisitDeclStmt(
const DeclStmt *S) {
2117 LVarCtx = Analyzer->LocalVarMap.getNextContext(CtxIndex, S, LVarCtx);
2119 for (
auto *
D : S->getDeclGroup()) {
2120 if (
auto *VD = dyn_cast_or_null<VarDecl>(
D)) {
2121 const Expr *
E = VD->getInit();
2127 if (
auto *EWC = dyn_cast<ExprWithCleanups>(
E))
2131 if (
auto Object = Analyzer->ConstructedObjects.find(
E);
2132 Object != Analyzer->ConstructedObjects.end()) {
2133 Object->second->setClangDecl(VD);
2134 Analyzer->ConstructedObjects.erase(Object);
2140void BuildLockset::VisitMaterializeTemporaryExpr(
2143 if (
auto Object = Analyzer->ConstructedObjects.find(
2145 Object != Analyzer->ConstructedObjects.end()) {
2146 Object->second->setClangDecl(ExtD);
2147 Analyzer->ConstructedObjects.erase(Object);
2152void BuildLockset::VisitReturnStmt(
const ReturnStmt *S) {
2153 if (Analyzer->CurrentFunction ==
nullptr)
2155 const Expr *RetVal = S->getRetValue();
2162 Analyzer->CurrentFunction->getReturnType().getCanonicalType();
2164 Analyzer->checkAccess(
2165 FunctionExitFSet, RetVal,
2176bool ThreadSafetyAnalyzer::join(
const FactEntry &A,
const FactEntry &B,
2178 if (A.kind() != B.kind()) {
2181 if ((A.managed() || A.asserted()) && (B.managed() || B.asserted())) {
2183 bool ShouldTakeB = B.kind() ==
LK_Shared;
2184 if (CanModify || !ShouldTakeB)
2193 return CanModify && A.asserted() && !B.asserted();
2211void ThreadSafetyAnalyzer::intersectAndWarn(FactSet &EntrySet,
2212 const FactSet &ExitSet,
2216 FactSet EntrySetOrig = EntrySet;
2219 for (
const auto &Fact : ExitSet) {
2220 const FactEntry &ExitFact = FactMan[Fact];
2222 FactSet::iterator EntryIt = EntrySet.findLockIter(FactMan, ExitFact);
2223 if (EntryIt != EntrySet.end()) {
2224 if (join(FactMan[*EntryIt], ExitFact,
2227 }
else if (!ExitFact.managed()) {
2228 ExitFact.handleRemovalFromIntersection(ExitSet, FactMan, JoinLoc,
2234 for (
const auto &Fact : EntrySetOrig) {
2235 const FactEntry *EntryFact = &FactMan[Fact];
2236 const FactEntry *ExitFact = ExitSet.findLock(FactMan, *EntryFact);
2240 EntryFact->handleRemovalFromIntersection(EntrySetOrig, FactMan, JoinLoc,
2243 EntrySet.removeLock(FactMan, *EntryFact);
2256 if (std::optional<CFGStmt> S =
Last.getAs<
CFGStmt>()) {
2257 if (isa<CXXThrowExpr>(S->getStmt()))
2272 if (!walker.
init(AC))
2280 CurrentFunction = dyn_cast<FunctionDecl>(
D);
2282 if (
D->
hasAttr<NoThreadSafetyAnalysisAttr>())
2289 if (isa<CXXConstructorDecl>(
D))
2291 if (isa<CXXDestructorDecl>(
D))
2297 CFGBlockInfo::getEmptyBlockInfo(LocalVarMap));
2309 Initial.Reachable =
true;
2312 LocalVarMap.traverseCFG(CFGraph, SortedGraph, BlockInfo);
2317 CapExprSet ExclusiveLocksAcquired;
2318 CapExprSet SharedLocksAcquired;
2319 CapExprSet LocksReleased;
2326 FactSet &InitialLockset = Initial.EntrySet;
2328 CapExprSet ExclusiveLocksToAdd;
2329 CapExprSet SharedLocksToAdd;
2334 if (
const auto *A = dyn_cast<RequiresCapabilityAttr>(
Attr)) {
2335 getMutexIDs(A->isShared() ? SharedLocksToAdd : ExclusiveLocksToAdd, A,
2337 }
else if (
const auto *A = dyn_cast<ReleaseCapabilityAttr>(
Attr)) {
2340 if (A->args_size() == 0)
2342 getMutexIDs(A->isShared() ? SharedLocksToAdd : ExclusiveLocksToAdd, A,
2344 getMutexIDs(LocksReleased, A,
nullptr,
D);
2345 }
else if (
const auto *A = dyn_cast<AcquireCapabilityAttr>(
Attr)) {
2346 if (A->args_size() == 0)
2348 getMutexIDs(A->isShared() ? SharedLocksAcquired
2349 : ExclusiveLocksAcquired,
2351 }
else if (isa<ExclusiveTrylockFunctionAttr>(
Attr)) {
2354 }
else if (isa<SharedTrylockFunctionAttr>(
Attr)) {
2357 }
else if (isa<TryAcquireCapabilityAttr>(
Attr)) {
2364 for (
const auto &Mu : ExclusiveLocksToAdd) {
2365 auto Entry = std::make_unique<LockableFactEntry>(Mu,
LK_Exclusive,
Loc,
2366 FactEntry::Declared);
2367 addLock(InitialLockset, std::move(Entry),
true);
2369 for (
const auto &Mu : SharedLocksToAdd) {
2370 auto Entry = std::make_unique<LockableFactEntry>(Mu,
LK_Shared,
Loc,
2371 FactEntry::Declared);
2372 addLock(InitialLockset, std::move(Entry),
true);
2378 FactSet ExpectedFunctionExitSet = Initial.EntrySet;
2384 for (
const auto &Lock : ExclusiveLocksAcquired)
2385 ExpectedFunctionExitSet.addLock(
2386 FactMan, std::make_unique<LockableFactEntry>(Lock,
LK_Exclusive,
2388 for (
const auto &Lock : SharedLocksAcquired)
2389 ExpectedFunctionExitSet.addLock(
2392 for (
const auto &Lock : LocksReleased)
2393 ExpectedFunctionExitSet.removeLock(FactMan, Lock);
2395 for (
const auto *CurrBlock : *SortedGraph) {
2396 unsigned CurrBlockID = CurrBlock->
getBlockID();
2397 CFGBlockInfo *CurrBlockInfo = &BlockInfo[CurrBlockID];
2400 VisitedBlocks.insert(CurrBlock);
2415 bool LocksetInitialized =
false;
2417 PE = CurrBlock->
pred_end(); PI != PE; ++PI) {
2419 if (*PI ==
nullptr || !VisitedBlocks.alreadySet(*PI))
2422 unsigned PrevBlockID = (*PI)->getBlockID();
2423 CFGBlockInfo *PrevBlockInfo = &BlockInfo[PrevBlockID];
2430 CurrBlockInfo->Reachable =
true;
2432 FactSet PrevLockset;
2433 getEdgeLockset(PrevLockset, PrevBlockInfo->ExitSet, *PI, CurrBlock);
2435 if (!LocksetInitialized) {
2436 CurrBlockInfo->EntrySet = PrevLockset;
2437 LocksetInitialized =
true;
2443 CurrBlockInfo->EntrySet, PrevLockset, CurrBlockInfo->EntryLoc,
2444 isa_and_nonnull<ContinueStmt>((*PI)->getTerminatorStmt())
2451 if (!CurrBlockInfo->Reachable)
2454 BuildLockset LocksetBuilder(
this, *CurrBlockInfo, ExpectedFunctionExitSet);
2457 for (
const auto &BI : *CurrBlock) {
2458 switch (BI.getKind()) {
2461 LocksetBuilder.Visit(CS.
getStmt());
2468 if (!DD->hasAttrs())
2471 LocksetBuilder.handleCall(
nullptr, DD,
2479 LocksetBuilder.handleCall(
nullptr,
CF.getFunctionDecl(),
2480 SxBuilder.createVariable(
CF.getVarDecl()),
2481 CF.getVarDecl()->getLocation());
2490 if (
auto Object = ConstructedObjects.find(
2491 TD.getBindTemporaryExpr()->getSubExpr());
2492 Object != ConstructedObjects.end()) {
2496 LocksetBuilder.handleCall(
nullptr, DD,
Object->second,
2497 TD.getBindTemporaryExpr()->getEndLoc());
2498 ConstructedObjects.erase(Object);
2506 CurrBlockInfo->ExitSet = LocksetBuilder.FSet;
2513 SE = CurrBlock->succ_end(); SI != SE; ++SI) {
2515 if (*SI ==
nullptr || !VisitedBlocks.alreadySet(*SI))
2519 CFGBlockInfo *PreLoop = &BlockInfo[FirstLoopBlock->
getBlockID()];
2520 CFGBlockInfo *LoopEnd = &BlockInfo[CurrBlockID];
2521 intersectAndWarn(PreLoop->EntrySet, LoopEnd->ExitSet, PreLoop->EntryLoc,
2527 if (!Final.Reachable)
2531 intersectAndWarn(ExpectedFunctionExitSet, Final.ExitSet, Final.ExitLoc,
2547 ThreadSafetyAnalyzer Analyzer(Handler, *BSet);
2548 Analyzer.runAnalysis(AC);
2562 llvm_unreachable(
"Unknown AccessKind");
This file defines AnalysisDeclContext, a class that manages the analysis context data for context sen...
Defines enum values for all the target-independent builtin functions.
enum clang::sema::@1655::IndirectLocalPathEntry::EntryKind Kind
static void dump(llvm::raw_ostream &OS, StringRef FunctionName, ArrayRef< CounterExpression > Expressions, ArrayRef< CounterMappingRegion > Regions)
static Decl::Kind getKind(const Decl *D)
Defines the C++ Decl subclasses, other than those for templates (found in DeclTemplate....
Defines the clang::Expr interface and subclasses for C++ expressions.
llvm::DenseSet< const void * > Visited
Forward-declares and imports various common LLVM datatypes that clang wants to use unqualified.
Defines an enumeration for C++ overloaded operators.
static std::string toString(const clang::SanitizerSet &Sanitizers)
Produce a string containing comma-separated names of sanitizers in Sanitizers set.
Defines the clang::SourceLocation class and associated facilities.
Defines various enumerations that describe declaration and type specifiers.
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 getStaticBooleanValue(Expr *E, bool &TCond)
static bool neverReturns(const CFGBlock *B)
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.
static const ValueDecl * getValueDecl(const Expr *Exp)
Gets the value decl pointer from DeclRefExprs or MemberExprs.
static const Expr * UnpackConstruction(const Expr *E)
C Language Family Type Representation.
AnalysisDeclContext contains the context data for the function, method or block under analysis.
Attr - This represents one attribute.
attr::Kind getKind() const
SourceLocation getLocation() const
A builtin binary operation expression such as "x + y" or "x <= y".
static bool isAssignmentOp(Opcode Opc)
Represents C++ object destructor implicitly generated for automatic object or temporary bound to cons...
const VarDecl * getVarDecl() const
const Stmt * getTriggerStmt() const
Represents a single basic block in a source-level CFG.
bool hasNoReturnElement() const
succ_iterator succ_begin()
Stmt * getTerminatorStmt()
AdjacentBlocks::const_iterator const_pred_iterator
pred_iterator pred_begin()
unsigned getBlockID() const
Stmt * getTerminatorCondition(bool StripParens=true)
AdjacentBlocks::const_iterator const_succ_iterator
Represents a top-level expression in a basic block.
T castAs() const
Convert to the specified CFGElement type, asserting that this CFGElement is of the desired type.
const CXXDestructorDecl * getDestructorDecl(ASTContext &astContext) const
const Stmt * getStmt() const
Represents C++ object destructor implicitly generated at the end of full expression for temporary obj...
Represents a source-level, intra-procedural CFG that represents the control-flow of a Stmt.
unsigned getNumBlockIDs() const
Returns the total number of BlockIDs allocated (which start at 0).
Represents a call to a C++ constructor.
Expr * getArg(unsigned Arg)
Return the specified argument.
CXXConstructorDecl * getConstructor() const
Get the constructor that this expression will (ultimately) call.
Represents a C++ constructor within a class.
Represents a static or instance method of a struct/union/class.
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
FunctionDecl * getDirectCallee()
If the callee is a FunctionDecl, return it. Otherwise return null.
CastExpr - Base class for type casts, including both implicit casts (ImplicitCastExpr) and explicit c...
CastKind getCastKind() const
ConstStmtVisitor - This class implements a simple visitor for Stmt subclasses.
DeclStmt - Adaptor class for mixing declarations with statements and expressions.
llvm::iterator_range< specific_attr_iterator< T > > specific_attrs() const
SourceLocation getLocation() const
bool isDefinedOutsideFunctionOrMethod() const
isDefinedOutsideFunctionOrMethod - This predicate returns true if this scoped decl is defined outside...
DeclContext * getDeclContext()
This represents one expression.
Expr * IgnoreParenCasts() LLVM_READONLY
Skip past any parentheses and casts which might surround this expression until reaching a fixed point...
Expr * IgnoreImplicit() LLVM_READONLY
Skip past any implicit AST nodes which might surround this expression until reaching a fixed point.
Expr * IgnoreParens() LLVM_READONLY
Skip past any parentheses which might surround this expression until reaching a fixed point.
SourceLocation getExprLoc() const LLVM_READONLY
getExprLoc - Return the preferred location for the arrow when diagnosing a problem with a generic exp...
Represents a function declaration or definition.
ArrayRef< ParmVarDecl * > parameters() const
Represents a prvalue temporary that is written into memory so that a reference can bind to it.
Expr * getSubExpr() const
Retrieve the temporary-generating subexpression whose value will be materialized into a glvalue.
ValueDecl * getExtendingDecl()
Get the declaration which triggered the lifetime-extension of this temporary, if any.
This represents a decl that may have a name.
StringRef getName() const
Get the name of identifier for this declaration as a StringRef.
Implements a set of CFGBlocks using a BitVector.
A (possibly-)qualified type.
bool isConstQualified() const
Determine whether this type is const-qualified.
ReturnStmt - This represents a return, optionally of an expression: return; return 4;.
Encodes a location in the source.
bool isValid() const
Return true if this is a valid SourceLocation object.
Stmt - This represents one statement.
SourceLocation getEndLoc() const LLVM_READONLY
void dump() const
Dumps the specified AST fragment and all subtrees to llvm::errs().
bool isReferenceType() const
QualType getPointeeType() const
If this is a pointer, ObjC object pointer, or block pointer, this returns the respective pointee.
bool isLValueReferenceType() const
const T * getAs() const
Member-template getAs<specific type>'.
UnaryOperator - This represents the unary-expression's (except sizeof and alignof),...
Expr * getSubExpr() const
Represent the declaration of a variable (in which case it is an lvalue) a function (in which case it ...
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.
BeforeInfo * insertAttrExprs(const ValueDecl *Vd, ThreadSafetyAnalyzer &Analyzer)
Process acquired_before and acquired_after attributes on Vd.
BeforeInfo * getBeforeInfoForDecl(const ValueDecl *Vd, ThreadSafetyAnalyzer &Analyzer)
const PostOrderCFGView * getSortedGraph() const
const NamedDecl * getDecl() const
bool init(AnalysisDeclContext &AC)
const CFG * getGraph() const
bool shouldIgnore() const
bool equals(const CapabilityExpr &other) const
const til::SExpr * sexpr() const
std::string toString() const
const ValueDecl * valueDecl() const
StringRef getKind() const
Handler class for thread safety warnings.
virtual ~ThreadSafetyHandler()
virtual void handleInvalidLockExp(SourceLocation Loc)
Warn about lock expressions which fail to resolve to lockable objects.
virtual void enterFunction(const FunctionDecl *FD)
Called by the analysis when starting analysis of a function.
virtual void handleIncorrectUnlockKind(StringRef Kind, Name LockName, LockKind Expected, LockKind Received, SourceLocation LocLocked, SourceLocation LocUnlock)
Warn about an unlock function call that attempts to unlock a lock with the incorrect lock kind.
virtual void leaveFunction(const FunctionDecl *FD)
Called by the analysis when finishing analysis of a function.
virtual void handleExclusiveAndShared(StringRef Kind, Name LockName, SourceLocation Loc1, SourceLocation Loc2)
Warn when a mutex is held exclusively and shared at the same point.
virtual void handleMutexNotHeld(StringRef Kind, const NamedDecl *D, ProtectedOperationKind POK, Name LockName, LockKind LK, SourceLocation Loc, Name *PossibleMatch=nullptr)
Warn when a protected operation occurs while the specific mutex protecting the operation is not locke...
virtual void handleFunExcludesLock(StringRef Kind, Name FunName, Name LockName, SourceLocation Loc)
Warn when a function is called while an excluded mutex is locked.
virtual void handleNoMutexHeld(const NamedDecl *D, ProtectedOperationKind POK, AccessKind AK, SourceLocation Loc)
Warn when a protected operation occurs while no locks are held.
virtual void handleUnmatchedUnlock(StringRef Kind, Name LockName, SourceLocation Loc, SourceLocation LocPreviousUnlock)
Warn about unlock function calls that do not have a prior matching lock expression.
virtual void handleNegativeNotHeld(StringRef Kind, Name LockName, Name Neg, SourceLocation Loc)
Warn when acquiring a lock that the negative capability is not held.
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.
virtual void handleDoubleLock(StringRef Kind, Name LockName, SourceLocation LocLocked, SourceLocation LocDoubleLock)
Warn about lock function calls for locks which are already held.
A Literal pointer to an object allocated in memory.
Base class for AST nodes in the typed intermediate language.
internal::Matcher< T > traverse(TraversalKind TK, const internal::Matcher< T > &InnerMatcher)
Causes all nested matchers to be matched with the specified traversal kind.
unsigned kind
All of the diagnostics that can be emitted by the frontend.
@ CF
Indicates that the tracked object is a CF object.
bool Dec(InterpState &S, CodePtr OpPC)
1) Pops a pointer from the stack 2) Load the value from the pointer 3) Writes the value decreased by ...
bool Neg(InterpState &S, CodePtr OpPC)
bool matches(const til::SExpr *E1, const til::SExpr *E2)
LockKind getLockKindFromAccessKind(AccessKind AK)
Helper function that returns a LockKind required for the given level of access.
@ LEK_NotLockedAtEndOfFunction
@ LEK_LockedSomePredecessors
@ LEK_LockedAtEndOfFunction
@ LEK_LockedSomeLoopIterations
void threadSafetyCleanup(BeforeSet *Cache)
AccessKind
This enum distinguishes between different ways to access (read or write) a variable.
@ AK_Written
Writing a variable.
@ AK_Read
Reading a variable.
LockKind
This enum distinguishes between different kinds of lock actions.
@ LK_Shared
Shared/reader lock of a mutex.
@ LK_Exclusive
Exclusive/writer lock of a mutex.
@ LK_Generic
Can be either Shared or Exclusive.
void runThreadSafetyAnalysis(AnalysisDeclContext &AC, ThreadSafetyHandler &Handler, BeforeSet **Bset)
Check a function's CFG for thread-safety violations.
ProtectedOperationKind
This enum distinguishes between different kinds of operations that may need to be protected by locks.
@ POK_PtPassByRef
Passing a pt-guarded variable by reference.
@ POK_VarDereference
Dereferencing a variable (e.g. p in *p = 5;)
@ POK_PassByRef
Passing a guarded variable by reference.
@ POK_ReturnByRef
Returning a guarded variable by reference.
@ POK_VarAccess
Reading or writing a variable (e.g. x in x = 5;)
@ POK_FunctionCall
Making a function call (e.g. fool())
@ POK_PtReturnByRef
Returning a pt-guarded variable by reference.
The JSON file list parser is used to communicate input to InstallAPI.
@ Self
'self' clause, allowed on Compute and Combined Constructs, plus 'update'.
OverloadedOperatorKind
Enumeration specifying the different kinds of C++ overloaded operators.
@ Result
The result type of a method or function.
const FunctionProtoType * T
Iterator for iterating over Stmt * arrays that contain only T *.