clang: lib/Analysis/ThreadSafety.cpp Source File (original) (raw)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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"
50#include
51#include
52#include
53#include
54#include
55#include
56#include
57#include <type_traits>
58#include
59#include
60
61using namespace clang;
62using namespace threadSafety;
63
64
66
67
70 const Expr *DeclExp, StringRef Kind) {
72 if (DeclExp)
74
75
78}
79
80namespace {
81
82
83
84class CapExprSet : public SmallVector<CapabilityExpr, 4> {
85public:
86
88 if (llvm::none_of(*this, [=](const CapabilityExpr &CapE2) {
89 return CapE.equals(CapE2);
90 }))
91 push_back(CapE);
92 }
93};
94
95class FactManager;
96class FactSet;
97
98
99
100
101
102
103
105public:
106 enum FactEntryKind { Lockable, ScopedLockable };
107
108
109 enum SourceKind {
110 Acquired,
111 Asserted,
112 Declared,
113 Managed,
114 };
115
116private:
117 const FactEntryKind Kind : 8;
118
119
121
122
123 SourceKind Source : 8;
124
125
127
128public:
132 virtual ~FactEntry() = default;
133
136 FactEntryKind getFactEntryKind() const { return Kind; }
137
138 bool asserted() const { return Source == Asserted; }
139 bool declared() const { return Source == Declared; }
140 bool managed() const { return Source == Managed; }
141
142 virtual void
143 handleRemovalFromIntersection(const FactSet &FSet, FactManager &FactMan,
146 virtual void handleLock(FactSet &FSet, FactManager &FactMan,
147 const FactEntry &entry,
149 virtual void handleUnlock(FactSet &FSet, FactManager &FactMan,
151 bool FullyRemove,
153
154
155 bool isAtLeast(LockKind LK) const {
157 }
158};
159
160using FactID = unsigned short;
161
162
163
164class FactManager {
165private:
166 std::vector<std::unique_ptr> Facts;
167
168public:
169 FactID newFact(std::unique_ptr Entry) {
170 Facts.push_back(std::move(Entry));
171 return static_cast<unsigned short>(Facts.size() - 1);
172 }
173
174 const FactEntry &operator[](FactID F) const { return *Facts[F]; }
175};
176
177
178
179
180
181
182
183
184class FactSet {
185private:
187
188 FactVec FactIDs;
189
190public:
191 using iterator = FactVec::iterator;
192 using const_iterator = FactVec::const_iterator;
193
194 iterator begin() { return FactIDs.begin(); }
195 const_iterator begin() const { return FactIDs.begin(); }
196
197 iterator end() { return FactIDs.end(); }
198 const_iterator end() const { return FactIDs.end(); }
199
200 bool isEmpty() const { return FactIDs.size() == 0; }
201
202
203 bool isEmpty(FactManager &FactMan) const {
204 for (const auto FID : *this) {
205 if (!FactMan[FID].negative())
206 return false;
207 }
208 return true;
209 }
210
211 void addLockByID(FactID ID) { FactIDs.push_back(ID); }
212
213 FactID addLock(FactManager &FM, std::unique_ptr Entry) {
214 FactID F = FM.newFact(std::move(Entry));
215 FactIDs.push_back(F);
216 return F;
217 }
218
219 bool removeLock(FactManager& FM, const CapabilityExpr &CapE) {
220 unsigned n = FactIDs.size();
221 if (n == 0)
222 return false;
223
224 for (unsigned i = 0; i < n-1; ++i) {
225 if (FM[FactIDs[i]].matches(CapE)) {
226 FactIDs[i] = FactIDs[n-1];
227 FactIDs.pop_back();
228 return true;
229 }
230 }
231 if (FM[FactIDs[n-1]].matches(CapE)) {
232 FactIDs.pop_back();
233 return true;
234 }
235 return false;
236 }
237
238 iterator findLockIter(FactManager &FM, const CapabilityExpr &CapE) {
239 return std::find_if(begin(), end(), [&](FactID ID) {
240 return FM[ID].matches(CapE);
241 });
242 }
243
244 const FactEntry *findLock(FactManager &FM, const CapabilityExpr &CapE) const {
245 auto I = std::find_if(begin(), end(), [&](FactID ID) {
246 return FM[ID].matches(CapE);
247 });
248 return I != end() ? &FM[*I] : nullptr;
249 }
250
251 const FactEntry *findLockUniv(FactManager &FM,
253 auto I = std::find_if(begin(), end(), [&](FactID ID) -> bool {
254 return FM[ID].matchesUniv(CapE);
255 });
256 return I != end() ? &FM[*I] : nullptr;
257 }
258
259 const FactEntry *findPartialMatch(FactManager &FM,
261 auto I = std::find_if(begin(), end(), [&](FactID ID) -> bool {
262 return FM[ID].partiallyMatches(CapE);
263 });
264 return I != end() ? &FM[*I] : nullptr;
265 }
266
267 bool containsMutexDecl(FactManager &FM, const ValueDecl* Vd) const {
268 auto I = std::find_if(begin(), end(), [&](FactID ID) -> bool {
269 return FM[ID].valueDecl() == Vd;
270 });
271 return I != end();
272 }
273};
274
275class ThreadSafetyAnalyzer;
276
277}
278
280namespace threadSafety {
281
283private:
285
286 struct BeforeInfo {
289
290 BeforeInfo() = default;
291 BeforeInfo(BeforeInfo &&) = default;
292 };
293
294 using BeforeMap =
295 llvm::DenseMap<const ValueDecl *, std::unique_ptr>;
296 using CycleMap = llvm::DenseMap<const ValueDecl *, bool>;
297
298public:
300
302 ThreadSafetyAnalyzer& Analyzer);
303
305 ThreadSafetyAnalyzer &Analyzer);
306
308 const FactSet& FSet,
309 ThreadSafetyAnalyzer& Analyzer,
311
312private:
313 BeforeMap BMap;
314 CycleMap CycMap;
315};
316
317}
318}
319
320namespace {
321
322class LocalVariableMap;
323
324using LocalVarContext = llvm::ImmutableMap<const NamedDecl *, unsigned>;
325
326
327enum CFGBlockSide { CBS_Entry, CBS_Exit };
328
329
330
331
332struct CFGBlockInfo {
333
334 FactSet EntrySet;
335
336
337 FactSet ExitSet;
338
339
340 LocalVarContext EntryContext;
341
342
343 LocalVarContext ExitContext;
344
345
347
348
350
351
352 unsigned EntryIndex;
353
354
355 bool Reachable = false;
356
357 const FactSet &getSet(CFGBlockSide Side) const {
358 return Side == CBS_Entry ? EntrySet : ExitSet;
359 }
360
361 SourceLocation getLocation(CFGBlockSide Side) const {
362 return Side == CBS_Entry ? EntryLoc : ExitLoc;
363 }
364
365private:
366 CFGBlockInfo(LocalVarContext EmptyCtx)
367 : EntryContext(EmptyCtx), ExitContext(EmptyCtx) {}
368
369public:
370 static CFGBlockInfo getEmptyBlockInfo(LocalVariableMap &M);
371};
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386class LocalVariableMap {
387public:
388 using Context = LocalVarContext;
389
390
391
392
393
394 struct VarDefinition {
395 public:
396 friend class LocalVariableMap;
397
398
400
401
402 const Expr *Exp = nullptr;
403
404
405 unsigned Ref = 0;
406
407
408 Context Ctx;
409
410 bool isReference() const { return !Exp; }
411
412 private:
413
415 : Dec(D), Exp(E), Ctx(C) {}
416
417
418 VarDefinition(const NamedDecl *D, unsigned R, Context C)
419 : Dec(D), Ref(R), Ctx(C) {}
420 };
421
422private:
423 Context::Factory ContextFactory;
424 std::vector VarDefinitions;
425 std::vector<std::pair<const Stmt *, Context>> SavedContexts;
426
427public:
428 LocalVariableMap() {
429
430 VarDefinitions.push_back(VarDefinition(nullptr, 0u, getEmptyContext()));
431 }
432
433
434 const VarDefinition* lookup(const NamedDecl *D, Context Ctx) {
435 const unsigned *i = Ctx.lookup(D);
436 if (!i)
437 return nullptr;
438 assert(*i < VarDefinitions.size());
439 return &VarDefinitions[*i];
440 }
441
442
443
444
445 const Expr* lookupExpr(const NamedDecl *D, Context &Ctx) {
446 const unsigned *P = Ctx.lookup(D);
447 if ()
448 return nullptr;
449
450 unsigned i = *P;
451 while (i > 0) {
452 if (VarDefinitions[i].Exp) {
453 Ctx = VarDefinitions[i].Ctx;
454 return VarDefinitions[i].Exp;
455 }
456 i = VarDefinitions[i].Ref;
457 }
458 return nullptr;
459 }
460
461 Context getEmptyContext() { return ContextFactory.getEmptyMap(); }
462
463
464
465
466 Context getNextContext(unsigned &CtxIndex, const Stmt *S, Context C) {
467 if (SavedContexts[CtxIndex+1].first == S) {
468 CtxIndex++;
469 Context Result = SavedContexts[CtxIndex].second;
470 return Result;
471 }
472 return C;
473 }
474
475 void dumpVarDefinitionName(unsigned i) {
476 if (i == 0) {
477 llvm::errs() << "Undefined";
478 return;
479 }
480 const NamedDecl *Dec = VarDefinitions[i].Dec;
481 if (!Dec) {
482 llvm::errs() << "<>";
483 return;
484 }
485 Dec->printName(llvm::errs());
486 llvm::errs() << "." << i << " " << ((const void*) Dec);
487 }
488
489
490 void dump() {
491 for (unsigned i = 1, e = VarDefinitions.size(); i < e; ++i) {
492 const Expr *Exp = VarDefinitions[i].Exp;
493 unsigned Ref = VarDefinitions[i].Ref;
494
495 dumpVarDefinitionName(i);
496 llvm::errs() << " = ";
497 if (Exp) Exp->dump();
498 else {
499 dumpVarDefinitionName(Ref);
500 llvm::errs() << "\n";
501 }
502 }
503 }
504
505
506 void dumpContext(Context C) {
507 for (Context::iterator I = C.begin(), E = C.end(); I != E; ++I) {
509 D->printName(llvm::errs());
510 llvm::errs() << " -> ";
511 dumpVarDefinitionName(I.getData());
512 llvm::errs() << "\n";
513 }
514 }
515
516
518 std::vector &BlockInfo);
519
520protected:
521 friend class VarMapBuilder;
522
523
524 unsigned getContextIndex() { return SavedContexts.size()-1; }
525
526
527 void saveContext(const Stmt *S, Context C) {
528 SavedContexts.push_back(std::make_pair(S, C));
529 }
530
531
532
533 Context addDefinition(const NamedDecl *D, const Expr *Exp, Context Ctx) {
534 assert(!Ctx.contains(D));
535 unsigned newID = VarDefinitions.size();
536 Context NewCtx = ContextFactory.add(Ctx, D, newID);
537 VarDefinitions.push_back(VarDefinition(D, Exp, Ctx));
538 return NewCtx;
539 }
540
541
542 Context addReference(const NamedDecl *D, unsigned i, Context Ctx) {
543 unsigned newID = VarDefinitions.size();
544 Context NewCtx = ContextFactory.add(Ctx, D, newID);
545 VarDefinitions.push_back(VarDefinition(D, i, Ctx));
546 return NewCtx;
547 }
548
549
550
551 Context updateDefinition(const NamedDecl *D, Expr *Exp, Context Ctx) {
552 if (Ctx.contains(D)) {
553 unsigned newID = VarDefinitions.size();
554 Context NewCtx = ContextFactory.remove(Ctx, D);
555 NewCtx = ContextFactory.add(NewCtx, D, newID);
556 VarDefinitions.push_back(VarDefinition(D, Exp, Ctx));
557 return NewCtx;
558 }
559 return Ctx;
560 }
561
562
563
564 Context clearDefinition(const NamedDecl *D, Context Ctx) {
565 Context NewCtx = Ctx;
566 if (NewCtx.contains(D)) {
567 NewCtx = ContextFactory.remove(NewCtx, D);
568 NewCtx = ContextFactory.add(NewCtx, D, 0);
569 }
570 return NewCtx;
571 }
572
573
574 Context removeDefinition(const NamedDecl *D, Context Ctx) {
575 Context NewCtx = Ctx;
576 if (NewCtx.contains(D)) {
577 NewCtx = ContextFactory.remove(NewCtx, D);
578 }
579 return NewCtx;
580 }
581
582 Context intersectContexts(Context C1, Context C2);
583 Context createReferenceContext(Context C);
584 void intersectBackEdge(Context C1, Context C2);
585};
586
587}
588
589
590CFGBlockInfo CFGBlockInfo::getEmptyBlockInfo(LocalVariableMap &M) {
591 return CFGBlockInfo(M.getEmptyContext());
592}
593
594namespace {
595
596
597class VarMapBuilder : public ConstStmtVisitor {
598public:
599 LocalVariableMap* VMap;
600 LocalVariableMap::Context Ctx;
601
602 VarMapBuilder(LocalVariableMap *VM, LocalVariableMap::Context C)
603 : VMap(VM), Ctx(C) {}
604
605 void VisitDeclStmt(const DeclStmt *S);
607};
608
609}
610
611
612void VarMapBuilder::VisitDeclStmt(const DeclStmt *S) {
613 bool modifiedCtx = false;
615 for (const auto *D : DGrp) {
616 if (const auto *VD = dyn_cast_or_null(D)) {
617 const Expr *E = VD->getInit();
618
619
621 if (T.isTrivialType(VD->getASTContext())) {
622 Ctx = VMap->addDefinition(VD, E, Ctx);
623 modifiedCtx = true;
624 }
625 }
626 }
627 if (modifiedCtx)
628 VMap->saveContext(S, Ctx);
629}
630
631
632void VarMapBuilder::VisitBinaryOperator(const BinaryOperator *BO) {
634 return;
635
637
638
639 if (const auto *DRE = dyn_cast(LHSExp)) {
640 const ValueDecl *VDec = DRE->getDecl();
641 if (Ctx.lookup(VDec)) {
642 if (BO->getOpcode() == BO_Assign)
643 Ctx = VMap->updateDefinition(VDec, BO->getRHS(), Ctx);
644 else
645
646 Ctx = VMap->clearDefinition(VDec, Ctx);
647 VMap->saveContext(BO, Ctx);
648 }
649 }
650}
651
652
653
654
655LocalVariableMap::Context
656LocalVariableMap::intersectContexts(Context C1, Context C2) {
657 Context Result = C1;
658 for (const auto &P : C1) {
660 const unsigned *i2 = C2.lookup(Dec);
661 if (!i2)
662 Result = removeDefinition(Dec, Result);
663 else if (*i2 != P.second)
664 Result = clearDefinition(Dec, Result);
665 }
666 return Result;
667}
668
669
670
671
672LocalVariableMap::Context LocalVariableMap::createReferenceContext(Context C) {
673 Context Result = getEmptyContext();
675 Result = addReference(P.first, P.second, Result);
676 return Result;
677}
678
679
680
681
682void LocalVariableMap::intersectBackEdge(Context C1, Context C2) {
683 for (const auto &P : C1) {
684 unsigned i1 = P.second;
685 VarDefinition *VDef = &VarDefinitions[i1];
686 assert(VDef->isReference());
687
688 const unsigned *i2 = C2.lookup(P.first);
689 if (!i2 || (*i2 != i1))
690 VDef->Ref = 0;
691 }
692}
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731void LocalVariableMap::traverseCFG(CFG *CFGraph,
733 std::vector &BlockInfo) {
735
736 for (const auto *CurrBlock : *SortedGraph) {
737 unsigned CurrBlockID = CurrBlock->getBlockID();
738 CFGBlockInfo *CurrBlockInfo = &BlockInfo[CurrBlockID];
739
740 VisitedBlocks.insert(CurrBlock);
741
742
743 bool HasBackEdges = false;
744 bool CtxInit = true;
746 PE = CurrBlock->pred_end(); PI != PE; ++PI) {
747
748 if (*PI == nullptr || !VisitedBlocks.alreadySet(*PI)) {
749 HasBackEdges = true;
750 continue;
751 }
752
753 unsigned PrevBlockID = (*PI)->getBlockID();
754 CFGBlockInfo *PrevBlockInfo = &BlockInfo[PrevBlockID];
755
756 if (CtxInit) {
757 CurrBlockInfo->EntryContext = PrevBlockInfo->ExitContext;
758 CtxInit = false;
759 }
760 else {
761 CurrBlockInfo->EntryContext =
762 intersectContexts(CurrBlockInfo->EntryContext,
763 PrevBlockInfo->ExitContext);
764 }
765 }
766
767
768
769 if (HasBackEdges)
770 CurrBlockInfo->EntryContext =
771 createReferenceContext(CurrBlockInfo->EntryContext);
772
773
774 saveContext(nullptr, CurrBlockInfo->EntryContext);
775 CurrBlockInfo->EntryIndex = getContextIndex();
776
777
778 VarMapBuilder VMapBuilder(this, CurrBlockInfo->EntryContext);
779 for (const auto &BI : *CurrBlock) {
780 switch (BI.getKind()) {
783 VMapBuilder.Visit(CS.getStmt());
784 break;
785 }
786 default:
787 break;
788 }
789 }
790 CurrBlockInfo->ExitContext = VMapBuilder.Ctx;
791
792
794 SE = CurrBlock->succ_end(); SI != SE; ++SI) {
795
796 if (*SI == nullptr || !VisitedBlocks.alreadySet(*SI))
797 continue;
798
799 CFGBlock *FirstLoopBlock = *SI;
800 Context LoopBegin = BlockInfo[FirstLoopBlock->getBlockID()].EntryContext;
801 Context LoopEnd = CurrBlockInfo->ExitContext;
802 intersectBackEdge(LoopBegin, LoopEnd);
803 }
804 }
805
806
808 saveContext(nullptr, BlockInfo[exitID].ExitContext);
809}
810
811
812
815 std::vector &BlockInfo) {
816 for (const auto *CurrBlock : *SortedGraph) {
817 CFGBlockInfo *CurrBlockInfo = &BlockInfo[CurrBlock->getBlockID()];
818
819
820
821 if (const Stmt *S = CurrBlock->getTerminatorStmt()) {
822 CurrBlockInfo->EntryLoc = CurrBlockInfo->ExitLoc = S->getBeginLoc();
823 } else {
825 BE = CurrBlock->rend(); BI != BE; ++BI) {
826
827 if (std::optional CS = BI->getAs<CFGStmt>()) {
828 CurrBlockInfo->ExitLoc = CS->getStmt()->getBeginLoc();
829 break;
830 }
831 }
832 }
833
834 if (CurrBlockInfo->ExitLoc.isValid()) {
835
836
837 for (const auto &BI : *CurrBlock) {
838
839 if (std::optional CS = BI.getAs<CFGStmt>()) {
840 CurrBlockInfo->EntryLoc = CS->getStmt()->getBeginLoc();
841 break;
842 }
843 }
844 } else if (CurrBlock->pred_size() == 1 && *CurrBlock->pred_begin() &&
845 CurrBlock != &CFGraph->getExit()) {
846
847
848 CurrBlockInfo->EntryLoc = CurrBlockInfo->ExitLoc =
849 BlockInfo[(*CurrBlock->pred_begin())->getBlockID()].ExitLoc;
850 } else if (CurrBlock->succ_size() == 1 && *CurrBlock->succ_begin()) {
851
852
853 CurrBlockInfo->EntryLoc = CurrBlockInfo->ExitLoc =
854 BlockInfo[(*CurrBlock->succ_begin())->getBlockID()].EntryLoc;
855 }
856 }
857}
858
859namespace {
860
861class LockableFactEntry : public FactEntry {
862public:
864 SourceKind Src = Acquired)
865 : FactEntry(Lockable, CE, LK, Loc, Src) {}
866
867 void
868 handleRemovalFromIntersection(const FactSet &FSet, FactManager &FactMan,
871 if (!asserted() && !negative() && !isUniversal()) {
873 LEK);
874 }
875 }
876
877 void handleLock(FactSet &FSet, FactManager &FactMan, const FactEntry &entry,
879 Handler.handleDoubleLock(entry.getKind(), entry.toString(), loc(),
880 entry.loc());
881 }
882
883 void handleUnlock(FactSet &FSet, FactManager &FactMan,
885 bool FullyRemove,
887 FSet.removeLock(FactMan, Cp);
889 FSet.addLock(FactMan, std::make_unique(
891 }
892 }
893
894 static bool classof(const FactEntry *A) {
895 return A->getFactEntryKind() == Lockable;
896 }
897};
898
899class ScopedLockableFactEntry : public FactEntry {
900private:
901 enum UnderlyingCapabilityKind {
902 UCK_Acquired,
903 UCK_ReleasedShared,
904 UCK_ReleasedExclusive,
905 };
906
907 struct UnderlyingCapability {
909 UnderlyingCapabilityKind Kind;
910 };
911
913
914public:
916 SourceKind Src)
917 : FactEntry(ScopedLockable, CE, LK_Exclusive, Loc, Src) {}
918
919 CapExprSet getUnderlyingMutexes() const {
920 CapExprSet UnderlyingMutexesSet;
921 for (const UnderlyingCapability &UnderlyingMutex : UnderlyingMutexes)
922 UnderlyingMutexesSet.push_back(UnderlyingMutex.Cap);
923 return UnderlyingMutexesSet;
924 }
925
927 UnderlyingMutexes.push_back(UnderlyingCapability{M, UCK_Acquired});
928 }
929
931 UnderlyingMutexes.push_back(UnderlyingCapability{M, UCK_ReleasedExclusive});
932 }
933
935 UnderlyingMutexes.push_back(UnderlyingCapability{M, UCK_ReleasedShared});
936 }
937
938 void
939 handleRemovalFromIntersection(const FactSet &FSet, FactManager &FactMan,
943 return;
944
945 for (const auto &UnderlyingMutex : UnderlyingMutexes) {
946 const auto *Entry = FSet.findLock(FactMan, UnderlyingMutex.Cap);
947 if ((UnderlyingMutex.Kind == UCK_Acquired && Entry) ||
948 (UnderlyingMutex.Kind != UCK_Acquired && !Entry)) {
949
950
952 UnderlyingMutex.Cap.toString(), loc(),
953 JoinLoc, LEK);
954 }
955 }
956 }
957
958 void handleLock(FactSet &FSet, FactManager &FactMan, const FactEntry &entry,
960 for (const auto &UnderlyingMutex : UnderlyingMutexes) {
961 if (UnderlyingMutex.Kind == UCK_Acquired)
962 lock(FSet, FactMan, UnderlyingMutex.Cap, entry.kind(), entry.loc(),
963 &Handler);
964 else
965 unlock(FSet, FactMan, UnderlyingMutex.Cap, entry.loc(), &Handler);
966 }
967 }
968
969 void handleUnlock(FactSet &FSet, FactManager &FactMan,
971 bool FullyRemove,
973 assert(!Cp.negative() && "Managing object cannot be negative.");
974 for (const auto &UnderlyingMutex : UnderlyingMutexes) {
975
976
978 if (UnderlyingMutex.Kind == UCK_Acquired) {
979 unlock(FSet, FactMan, UnderlyingMutex.Cap, UnlockLoc, TSHandler);
980 } else {
981 LockKind kind = UnderlyingMutex.Kind == UCK_ReleasedShared
984 lock(FSet, FactMan, UnderlyingMutex.Cap, kind, UnlockLoc, TSHandler);
985 }
986 }
987 if (FullyRemove)
988 FSet.removeLock(FactMan, Cp);
989 }
990
991 static bool classof(const FactEntry *A) {
992 return A->getFactEntryKind() == ScopedLockable;
993 }
994
995private:
996 void lock(FactSet &FSet, FactManager &FactMan, const CapabilityExpr &Cp,
999 if (const FactEntry *Fact = FSet.findLock(FactMan, Cp)) {
1000 if (Handler)
1002 loc);
1003 } else {
1004 FSet.removeLock(FactMan, !Cp);
1005 FSet.addLock(FactMan,
1006 std::make_unique(Cp, kind, loc, Managed));
1007 }
1008 }
1009
1010 void unlock(FactSet &FSet, FactManager &FactMan, const CapabilityExpr &Cp,
1012 if (FSet.findLock(FactMan, Cp)) {
1013 FSet.removeLock(FactMan, Cp);
1014 FSet.addLock(FactMan, std::make_unique(
1016 } else if (Handler) {
1018 if (const FactEntry *Neg = FSet.findLock(FactMan, !Cp))
1019 PrevLoc = Neg->loc();
1021 }
1022 }
1023};
1024
1025
1026class ThreadSafetyAnalyzer {
1027 friend class BuildLockset;
1029
1030 llvm::BumpPtrAllocator Bpa;
1033
1036 LocalVariableMap LocalVarMap;
1037
1038 llvm::SmallDenseMap<const Expr *, til::LiteralPtr *> ConstructedObjects;
1039 FactManager FactMan;
1040 std::vector BlockInfo;
1041
1043
1044public:
1046 : Arena(&Bpa), SxBuilder(Arena), Handler(H), GlobalBeforeSet(Bset) {}
1047
1049
1050 void addLock(FactSet &FSet, std::unique_ptr Entry,
1051 bool ReqAttr = false);
1052 void removeLock(FactSet &FSet, const CapabilityExpr &CapE,
1054
1055 template
1056 void getMutexIDs(CapExprSet &Mtxs, AttrType *Attr, const Expr *Exp,
1058
1059 template
1060 void getMutexIDs(CapExprSet &Mtxs, AttrType *Attr, const Expr *Exp,
1063 Expr *BrE, bool Neg);
1064
1065 const CallExpr* getTrylockCallExpr(const Stmt *Cond, LocalVarContext C,
1066 bool &Negate);
1067
1068 void getEdgeLockset(FactSet &Result, const FactSet &ExitSet,
1071
1072 bool join(const FactEntry &a, const FactEntry &b, bool CanModify);
1073
1074 void intersectAndWarn(FactSet &EntrySet, const FactSet &ExitSet,
1077
1078 void intersectAndWarn(FactSet &EntrySet, const FactSet &ExitSet,
1080 intersectAndWarn(EntrySet, ExitSet, JoinLoc, LEK, LEK);
1081 }
1082
1084
1085 void warnIfMutexNotHeld(const FactSet &FSet, const NamedDecl *D,
1089 void warnIfMutexHeld(const FactSet &FSet, const NamedDecl *D, const Expr *Exp,
1092
1093 void checkAccess(const FactSet &FSet, const Expr *Exp, AccessKind AK,
1095 void checkPtAccess(const FactSet &FSet, const Expr *Exp, AccessKind AK,
1097};
1098
1099}
1100
1101
1103 ThreadSafetyAnalyzer& Analyzer) {
1104
1105 BeforeInfo *Info = nullptr;
1106 {
1107
1108
1109 std::unique_ptr &InfoPtr = BMap[Vd];
1110 if (!InfoPtr)
1111 InfoPtr.reset(new BeforeInfo());
1112 Info = InfoPtr.get();
1113 }
1114
1115 for (const auto *At : Vd->attrs()) {
1116 switch (At->getKind()) {
1117 case attr::AcquiredBefore: {
1118 const auto *A = cast(At);
1119
1120
1121 for (const auto *Arg : A->args()) {
1123 Analyzer.SxBuilder.translateAttrExpr(Arg, nullptr);
1125 Info->Vect.push_back(Cpvd);
1126 const auto It = BMap.find(Cpvd);
1127 if (It == BMap.end())
1129 }
1130 }
1131 break;
1132 }
1133 case attr::AcquiredAfter: {
1134 const auto *A = cast(At);
1135
1136
1137 for (const auto *Arg : A->args()) {
1139 Analyzer.SxBuilder.translateAttrExpr(Arg, nullptr);
1141
1143 ArgInfo->Vect.push_back(Vd);
1144 }
1145 }
1146 break;
1147 }
1148 default:
1149 break;
1150 }
1151 }
1152
1153 return Info;
1154}
1155
1156BeforeSet::BeforeInfo *
1158 ThreadSafetyAnalyzer &Analyzer) {
1159 auto It = BMap.find(Vd);
1160 BeforeInfo *Info = nullptr;
1161 if (It == BMap.end())
1163 else
1164 Info = It->second.get();
1165 assert(Info && "BMap contained nullptr?");
1166 return Info;
1167}
1168
1169
1171 const FactSet& FSet,
1172 ThreadSafetyAnalyzer& Analyzer,
1175
1176
1177
1179 if (!Vd)
1180 return false;
1181
1183
1184 if (Info->Visited == 1)
1185 return true;
1186
1187 if (Info->Visited == 2)
1188 return false;
1189
1190 if (Info->Vect.empty())
1191 return false;
1192
1193 InfoVect.push_back(Info);
1194 Info->Visited = 1;
1195 for (const auto *Vdb : Info->Vect) {
1196
1197 if (FSet.containsMutexDecl(Analyzer.FactMan, Vdb)) {
1198 StringRef L1 = StartVd->getName();
1199 StringRef L2 = Vdb->getName();
1200 Analyzer.Handler.handleLockAcquiredBefore(CapKind, L1, L2, Loc);
1201 }
1202
1204 if (CycMap.try_emplace(Vd, true).second) {
1205 StringRef L1 = Vd->getName();
1206 Analyzer.Handler.handleBeforeAfterCycle(L1, Vd->getLocation());
1207 }
1208 }
1209 }
1210 Info->Visited = 2;
1211 return false;
1212 };
1213
1215
1216 for (auto *Info : InfoVect)
1217 Info->Visited = 0;
1218}
1219
1220
1222 if (const auto *CE = dyn_cast(Exp))
1224
1225 if (const auto *DR = dyn_cast(Exp))
1226 return DR->getDecl();
1227
1228 if (const auto *ME = dyn_cast(Exp))
1229 return ME->getMemberDecl();
1230
1231 return nullptr;
1232}
1233
1234namespace {
1235
1236template
1237class has_arg_iterator_range {
1238 using yes = char[1];
1239 using no = char[2];
1240
1241 template
1242 static yes& test(Inner *I, decltype(I->args()) * = nullptr);
1243
1244 template <typename>
1245 static no& test(...);
1246
1247public:
1248 static const bool value = sizeof(test(nullptr)) == sizeof(yes);
1249};
1250
1251}
1252
1253bool ThreadSafetyAnalyzer::inCurrentScope(const CapabilityExpr &CapE) {
1255 assert(SExp && "Null expressions should be ignored");
1256
1257 if (const auto *LP = dyn_casttil::LiteralPtr(SExp)) {
1258 const ValueDecl *VD = LP->clangDecl();
1259
1261 return false;
1262
1264 return false;
1265
1266 return true;
1267 }
1268
1269
1270 if (const auto *P = dyn_casttil::Project(SExp)) {
1271 if (!isa_and_nonnull(CurrentFunction))
1272 return false;
1273 const ValueDecl *VD = P->clangDecl();
1275 }
1276
1277 return false;
1278}
1279
1280
1281
1282void ThreadSafetyAnalyzer::addLock(FactSet &FSet,
1283 std::unique_ptr Entry,
1284 bool ReqAttr) {
1285 if (Entry->shouldIgnore())
1286 return;
1287
1288 if (!ReqAttr && !Entry->negative()) {
1289
1291 const FactEntry *Nen = FSet.findLock(FactMan, NegC);
1292 if (Nen) {
1293 FSet.removeLock(FactMan, NegC);
1294 }
1295 else {
1296 if (inCurrentScope(*Entry) && !Entry->asserted())
1298 NegC.toString(), Entry->loc());
1299 }
1300 }
1301
1302
1304 !Entry->asserted() && !Entry->declared()) {
1305 GlobalBeforeSet->checkBeforeAfter(Entry->valueDecl(), FSet, *this,
1306 Entry->loc(), Entry->getKind());
1307 }
1308
1309
1310 if (const FactEntry *Cp = FSet.findLock(FactMan, *Entry)) {
1311 if (!Entry->asserted())
1312 Cp->handleLock(FSet, FactMan, *Entry, Handler);
1313 } else {
1314 FSet.addLock(FactMan, std::move(Entry));
1315 }
1316}
1317
1318
1319
1320void ThreadSafetyAnalyzer::removeLock(FactSet &FSet, const CapabilityExpr &Cp,
1322 bool FullyRemove, LockKind ReceivedKind) {
1324 return;
1325
1326 const FactEntry *LDat = FSet.findLock(FactMan, Cp);
1327 if (!LDat) {
1329 if (const FactEntry *Neg = FSet.findLock(FactMan, !Cp))
1330 PrevLoc = Neg->loc();
1332 PrevLoc);
1333 return;
1334 }
1335
1336
1337
1338 if (ReceivedKind != LK_Generic && LDat->kind() != ReceivedKind) {
1340 ReceivedKind, LDat->loc(), UnlockLoc);
1341 }
1342
1343 LDat->handleUnlock(FSet, FactMan, Cp, UnlockLoc, FullyRemove, Handler);
1344}
1345
1346
1347
1348template
1349void ThreadSafetyAnalyzer::getMutexIDs(CapExprSet &Mtxs, AttrType *Attr,
1352 if (Attr->args_size() == 0) {
1353
1357 return;
1358 }
1359
1361 Mtxs.push_back_nodup(Cp);
1362 return;
1363 }
1364
1365 for (const auto *Arg : Attr->args()) {
1369 continue;
1370 }
1371
1373 Mtxs.push_back_nodup(Cp);
1374 }
1375}
1376
1377
1378
1379
1380template
1381void ThreadSafetyAnalyzer::getMutexIDs(CapExprSet &Mtxs, AttrType *Attr,
1385 Expr *BrE, bool Neg) {
1386
1387 bool branch = false;
1388 if (const auto *BLE = dyn_cast_or_null(BrE))
1389 branch = BLE->getValue();
1390 else if (const auto *ILE = dyn_cast_or_null(BrE))
1391 branch = ILE->getValue().getBoolValue();
1392
1393 int branchnum = branch ? 0 : 1;
1394 if (Neg)
1395 branchnum = !branchnum;
1396
1397
1398 int i = 0;
1400 SE = PredBlock->succ_end(); SI != SE && i < 2; ++SI, ++i) {
1401 if (*SI == CurrBlock && i == branchnum)
1402 getMutexIDs(Mtxs, Attr, Exp, D);
1403 }
1404}
1405
1408 TCond = false;
1409 return true;
1410 } else if (const auto *BLE = dyn_cast(E)) {
1411 TCond = BLE->getValue();
1412 return true;
1413 } else if (const auto *ILE = dyn_cast(E)) {
1414 TCond = ILE->getValue().getBoolValue();
1415 return true;
1416 } else if (auto *CE = dyn_cast(E))
1418 return false;
1419}
1420
1421
1422
1423
1424const CallExpr* ThreadSafetyAnalyzer::getTrylockCallExpr(const Stmt *Cond,
1425 LocalVarContext C,
1426 bool &Negate) {
1427 if (!Cond)
1428 return nullptr;
1429
1430 if (const auto *CallExp = dyn_cast(Cond)) {
1431 if (CallExp->getBuiltinCallee() == Builtin::BI__builtin_expect)
1432 return getTrylockCallExpr(CallExp->getArg(0), C, Negate);
1433 return CallExp;
1434 }
1435 else if (const auto *PE = dyn_cast(Cond))
1436 return getTrylockCallExpr(PE->getSubExpr(), C, Negate);
1437 else if (const auto *CE = dyn_cast(Cond))
1438 return getTrylockCallExpr(CE->getSubExpr(), C, Negate);
1439 else if (const auto *FE = dyn_cast(Cond))
1440 return getTrylockCallExpr(FE->getSubExpr(), C, Negate);
1441 else if (const auto *DRE = dyn_cast(Cond)) {
1442 const Expr *E = LocalVarMap.lookupExpr(DRE->getDecl(), C);
1443 return getTrylockCallExpr(E, C, Negate);
1444 }
1445 else if (const auto *UOP = dyn_cast(Cond)) {
1446 if (UOP->getOpcode() == UO_LNot) {
1447 Negate = !Negate;
1448 return getTrylockCallExpr(UOP->getSubExpr(), C, Negate);
1449 }
1450 return nullptr;
1451 }
1452 else if (const auto *BOP = dyn_cast(Cond)) {
1453 if (BOP->getOpcode() == BO_EQ || BOP->getOpcode() == BO_NE) {
1454 if (BOP->getOpcode() == BO_NE)
1455 Negate = !Negate;
1456
1457 bool TCond = false;
1459 if (!TCond) Negate = !Negate;
1460 return getTrylockCallExpr(BOP->getLHS(), C, Negate);
1461 }
1462 TCond = false;
1464 if (!TCond) Negate = !Negate;
1465 return getTrylockCallExpr(BOP->getRHS(), C, Negate);
1466 }
1467 return nullptr;
1468 }
1469 if (BOP->getOpcode() == BO_LAnd) {
1470
1471 return getTrylockCallExpr(BOP->getRHS(), C, Negate);
1472 }
1473 if (BOP->getOpcode() == BO_LOr)
1474 return getTrylockCallExpr(BOP->getRHS(), C, Negate);
1475 return nullptr;
1476 } else if (const auto *COP = dyn_cast(Cond)) {
1477 bool TCond, FCond;
1480 if (TCond && !FCond)
1481 return getTrylockCallExpr(COP->getCond(), C, Negate);
1482 if (!TCond && FCond) {
1483 Negate = !Negate;
1484 return getTrylockCallExpr(COP->getCond(), C, Negate);
1485 }
1486 }
1487 }
1488 return nullptr;
1489}
1490
1491
1492
1493
1494void ThreadSafetyAnalyzer::getEdgeLockset(FactSet& Result,
1495 const FactSet &ExitSet,
1497 const CFGBlock *CurrBlock) {
1499
1501
1502 if (!Cond || isa(PredBlock->getTerminatorStmt()))
1503 return;
1504
1505 bool Negate = false;
1506 const CFGBlockInfo *PredBlockInfo = &BlockInfo[PredBlock->getBlockID()];
1507 const LocalVarContext &LVarCtx = PredBlockInfo->ExitContext;
1508
1509 const auto *Exp = getTrylockCallExpr(Cond, LVarCtx, Negate);
1510 if (!Exp)
1511 return;
1512
1513 auto *FunDecl = dyn_cast_or_null(Exp->getCalleeDecl());
1514 if(!FunDecl || !FunDecl->hasAttrs())
1515 return;
1516
1517 CapExprSet ExclusiveLocksToAdd;
1518 CapExprSet SharedLocksToAdd;
1519
1520
1521 for (const auto *Attr : FunDecl->attrs()) {
1523 case attr::TryAcquireCapability: {
1524 auto *A = cast(Attr);
1525 getMutexIDs(A->isShared() ? SharedLocksToAdd : ExclusiveLocksToAdd, A,
1526 Exp, FunDecl, PredBlock, CurrBlock, A->getSuccessValue(),
1527 Negate);
1528 break;
1529 };
1530 case attr::ExclusiveTrylockFunction: {
1531 const auto *A = cast(Attr);
1532 getMutexIDs(ExclusiveLocksToAdd, A, Exp, FunDecl, PredBlock, CurrBlock,
1533 A->getSuccessValue(), Negate);
1534 break;
1535 }
1536 case attr::SharedTrylockFunction: {
1537 const auto *A = cast(Attr);
1538 getMutexIDs(SharedLocksToAdd, A, Exp, FunDecl, PredBlock, CurrBlock,
1539 A->getSuccessValue(), Negate);
1540 break;
1541 }
1542 default:
1543 break;
1544 }
1545 }
1546
1547
1549 for (const auto &ExclusiveLockToAdd : ExclusiveLocksToAdd)
1550 addLock(Result, std::make_unique(ExclusiveLockToAdd,
1552 for (const auto &SharedLockToAdd : SharedLocksToAdd)
1553 addLock(Result, std::make_unique(SharedLockToAdd,
1555}
1556
1557namespace {
1558
1559
1560
1561
1562
1563
1565 friend class ThreadSafetyAnalyzer;
1566
1567 ThreadSafetyAnalyzer *Analyzer;
1568 FactSet FSet;
1569
1570 const FactSet &FunctionExitFSet;
1571 LocalVariableMap::Context LVarCtx;
1572 unsigned CtxIndex;
1573
1574
1575
1578 Analyzer->checkAccess(FSet, Exp, AK, POK);
1579 }
1582 Analyzer->checkPtAccess(FSet, Exp, AK, POK);
1583 }
1584
1585 void handleCall(const Expr *Exp, const NamedDecl *D,
1588 void examineArguments(const FunctionDecl *FD,
1591 bool SkipFirstParam = false);
1592
1593public:
1594 BuildLockset(ThreadSafetyAnalyzer *Anlzr, CFGBlockInfo &Info,
1595 const FactSet &FunctionExitFSet)
1596 : ConstStmtVisitor(), Analyzer(Anlzr), FSet(Info.EntrySet),
1597 FunctionExitFSet(FunctionExitFSet), LVarCtx(Info.EntryContext),
1598 CtxIndex(Info.EntryIndex) {}
1599
1600 void VisitUnaryOperator(const UnaryOperator *UO);
1602 void VisitCastExpr(const CastExpr *CE);
1603 void VisitCallExpr(const CallExpr *Exp);
1605 void VisitDeclStmt(const DeclStmt *S);
1607 void VisitReturnStmt(const ReturnStmt *S);
1608};
1609
1610}
1611
1612
1613
1614void ThreadSafetyAnalyzer::warnIfMutexNotHeld(
1619 CapabilityExpr Cp = SxBuilder.translateAttrExpr(MutexExp, D, Exp, Self);
1622 return;
1624 return;
1625 }
1626
1628
1629 const FactEntry *LDat = FSet.findLock(FactMan, !Cp);
1630 if (LDat) {
1632 (!Cp).toString(), Loc);
1633 return;
1634 }
1635
1636
1637
1638 if (!inCurrentScope(Cp))
1639 return;
1640
1641
1642 LDat = FSet.findLock(FactMan, Cp);
1643 if (!LDat) {
1645 }
1646 return;
1647 }
1648
1649 const FactEntry *LDat = FSet.findLockUniv(FactMan, Cp);
1650 bool NoError = true;
1651 if (!LDat) {
1652
1653 LDat = FSet.findPartialMatch(FactMan, Cp);
1654 if (LDat) {
1655
1656 std::string PartMatchStr = LDat->toString();
1657 StringRef PartMatchName(PartMatchStr);
1659 &PartMatchName);
1660 } else {
1661
1663 }
1664 NoError = false;
1665 }
1666
1667 if (NoError && LDat && !LDat->isAtLeast(LK)) {
1669 }
1670}
1671
1672
1673void ThreadSafetyAnalyzer::warnIfMutexHeld(const FactSet &FSet,
1675 Expr *MutexExp,
1678 CapabilityExpr Cp = SxBuilder.translateAttrExpr(MutexExp, D, Exp, Self);
1681 return;
1683 return;
1684 }
1685
1686 const FactEntry *LDat = FSet.findLock(FactMan, Cp);
1687 if (LDat) {
1690 }
1691}
1692
1693
1694
1695
1696
1697
1698void ThreadSafetyAnalyzer::checkAccess(const FactSet &FSet, const Expr *Exp,
1702
1704
1705
1706
1707 while (const auto *DRE = dyn_cast(Exp)) {
1708 const auto *VD = dyn_cast(DRE->getDecl()->getCanonicalDecl());
1710 if (const auto *E = VD->getInit()) {
1711
1712 if (E == Exp)
1713 break;
1714 Exp = E;
1715 continue;
1716 }
1717 }
1718 break;
1719 }
1720
1721 if (const auto *UO = dyn_cast(Exp)) {
1722
1723 if (UO->getOpcode() == UO_Deref)
1724 checkPtAccess(FSet, UO->getSubExpr(), AK, POK);
1725 return;
1726 }
1727
1728 if (const auto *BO = dyn_cast(Exp)) {
1730 case BO_PtrMemD:
1731 return checkAccess(FSet, BO->getLHS(), AK, POK);
1732 case BO_PtrMemI:
1733 return checkPtAccess(FSet, BO->getLHS(), AK, POK);
1734 default:
1735 return;
1736 }
1737 }
1738
1739 if (const auto *AE = dyn_cast(Exp)) {
1740 checkPtAccess(FSet, AE->getLHS(), AK, POK);
1741 return;
1742 }
1743
1744 if (const auto *ME = dyn_cast(Exp)) {
1745 if (ME->isArrow())
1746 checkPtAccess(FSet, ME->getBase(), AK, POK);
1747 else
1748 checkAccess(FSet, ME->getBase(), AK, POK);
1749 }
1750
1753 return;
1754
1755 if (D->hasAttr() && FSet.isEmpty(FactMan)) {
1757 }
1758
1759 for (const auto *I : D->specific_attrs())
1760 warnIfMutexNotHeld(FSet, D, Exp, AK, I->getArg(), POK, nullptr, Loc);
1761}
1762
1763
1764
1765void ThreadSafetyAnalyzer::checkPtAccess(const FactSet &FSet, const Expr *Exp,
1768 while (true) {
1769 if (const auto *PE = dyn_cast(Exp)) {
1770 Exp = PE->getSubExpr();
1771 continue;
1772 }
1773 if (const auto *CE = dyn_cast(Exp)) {
1774 if (CE->getCastKind() == CK_ArrayToPointerDecay) {
1775
1776
1777 checkAccess(FSet, CE->getSubExpr(), AK, POK);
1778 return;
1779 }
1780 Exp = CE->getSubExpr();
1781 continue;
1782 }
1783 break;
1784 }
1785
1786
1791
1794 return;
1795
1796 if (D->hasAttr() && FSet.isEmpty(FactMan))
1798
1799 for (auto const *I : D->specific_attrs())
1800 warnIfMutexNotHeld(FSet, D, Exp, AK, I->getArg(), PtPOK, nullptr,
1802}
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819void BuildLockset::handleCall(const Expr *Exp, const NamedDecl *D,
1821 CapExprSet ExclusiveLocksToAdd, SharedLocksToAdd;
1822 CapExprSet ExclusiveLocksToRemove, SharedLocksToRemove, GenericLocksToRemove;
1823 CapExprSet ScopedReqsAndExcludes;
1824
1825
1827 if (Exp) {
1828 assert();
1831 std::pair<til::LiteralPtr *, StringRef> Placeholder =
1832 Analyzer->SxBuilder.createThisPlaceholder(Exp);
1833 [[maybe_unused]] auto inserted =
1834 Analyzer->ConstructedObjects.insert({Exp, Placeholder.first});
1835 assert(inserted.second && "Are we visiting the same expression again?");
1836 if (isa(Exp))
1837 Self = Placeholder.first;
1838 if (TagT->getDecl()->hasAttr())
1839 Scp = CapabilityExpr(Placeholder.first, Placeholder.second, false);
1840 }
1841
1844 }
1845
1847 switch (At->getKind()) {
1848
1849
1850 case attr::AcquireCapability: {
1851 const auto *A = cast(At);
1852 Analyzer->getMutexIDs(A->isShared() ? SharedLocksToAdd
1853 : ExclusiveLocksToAdd,
1855 break;
1856 }
1857
1858
1859
1860
1861 case attr::AssertExclusiveLock: {
1862 const auto *A = cast(At);
1863
1864 CapExprSet AssertLocks;
1865 Analyzer->getMutexIDs(AssertLocks, A, Exp, D, Self);
1866 for (const auto &AssertLock : AssertLocks)
1867 Analyzer->addLock(
1868 FSet, std::make_unique(
1870 break;
1871 }
1872 case attr::AssertSharedLock: {
1873 const auto *A = cast(At);
1874
1875 CapExprSet AssertLocks;
1876 Analyzer->getMutexIDs(AssertLocks, A, Exp, D, Self);
1877 for (const auto &AssertLock : AssertLocks)
1878 Analyzer->addLock(
1879 FSet, std::make_unique(
1880 AssertLock, LK_Shared, Loc, FactEntry::Asserted));
1881 break;
1882 }
1883
1884 case attr::AssertCapability: {
1885 const auto *A = cast(At);
1886 CapExprSet AssertLocks;
1887 Analyzer->getMutexIDs(AssertLocks, A, Exp, D, Self);
1888 for (const auto &AssertLock : AssertLocks)
1889 Analyzer->addLock(FSet, std::make_unique(
1890 AssertLock,
1892 Loc, FactEntry::Asserted));
1893 break;
1894 }
1895
1896
1897
1898 case attr::ReleaseCapability: {
1899 const auto *A = cast(At);
1900 if (A->isGeneric())
1901 Analyzer->getMutexIDs(GenericLocksToRemove, A, Exp, D, Self);
1902 else if (A->isShared())
1903 Analyzer->getMutexIDs(SharedLocksToRemove, A, Exp, D, Self);
1904 else
1905 Analyzer->getMutexIDs(ExclusiveLocksToRemove, A, Exp, D, Self);
1906 break;
1907 }
1908
1909 case attr::RequiresCapability: {
1910 const auto *A = cast(At);
1911 for (auto *Arg : A->args()) {
1912 Analyzer->warnIfMutexNotHeld(FSet, D, Exp,
1915
1917 Analyzer->getMutexIDs(ScopedReqsAndExcludes, A, Exp, D, Self);
1918 }
1919 break;
1920 }
1921
1922 case attr::LocksExcluded: {
1923 const auto *A = cast(At);
1924 for (auto *Arg : A->args()) {
1925 Analyzer->warnIfMutexHeld(FSet, D, Exp, Arg, Self, Loc);
1926
1928 Analyzer->getMutexIDs(ScopedReqsAndExcludes, A, Exp, D, Self);
1929 }
1930 break;
1931 }
1932
1933
1934 default:
1935 break;
1936 }
1937 }
1938
1939 std::optionalCallExpr::const\_arg\_range Args;
1940 if (Exp) {
1941 if (const auto *CE = dyn_cast(Exp))
1942 Args = CE->arguments();
1943 else if (const auto *CE = dyn_cast(Exp))
1944 Args = CE->arguments();
1945 else
1946 llvm_unreachable("Unknown call kind");
1947 }
1948 const auto *CalledFunction = dyn_cast(D);
1949 if (CalledFunction && Args.has_value()) {
1950 for (auto [Param, Arg] : zip(CalledFunction->parameters(), *Args)) {
1951 CapExprSet DeclaredLocks;
1952 for (const Attr *At : Param->attrs()) {
1953 switch (At->getKind()) {
1954 case attr::AcquireCapability: {
1955 const auto *A = cast(At);
1956 Analyzer->getMutexIDs(A->isShared() ? SharedLocksToAdd
1957 : ExclusiveLocksToAdd,
1959 Analyzer->getMutexIDs(DeclaredLocks, A, Exp, D, Self);
1960 break;
1961 }
1962
1963 case attr::ReleaseCapability: {
1964 const auto *A = cast(At);
1965 if (A->isGeneric())
1966 Analyzer->getMutexIDs(GenericLocksToRemove, A, Exp, D, Self);
1967 else if (A->isShared())
1968 Analyzer->getMutexIDs(SharedLocksToRemove, A, Exp, D, Self);
1969 else
1970 Analyzer->getMutexIDs(ExclusiveLocksToRemove, A, Exp, D, Self);
1971 Analyzer->getMutexIDs(DeclaredLocks, A, Exp, D, Self);
1972 break;
1973 }
1974
1975 case attr::RequiresCapability: {
1976 const auto *A = cast(At);
1977 for (auto *Arg : A->args())
1978 Analyzer->warnIfMutexNotHeld(FSet, D, Exp,
1981 Analyzer->getMutexIDs(DeclaredLocks, A, Exp, D, Self);
1982 break;
1983 }
1984
1985 case attr::LocksExcluded: {
1986 const auto *A = cast(At);
1987 for (auto *Arg : A->args())
1988 Analyzer->warnIfMutexHeld(FSet, D, Exp, Arg, Self, Loc);
1989 Analyzer->getMutexIDs(DeclaredLocks, A, Exp, D, Self);
1990 break;
1991 }
1992
1993 default:
1994 break;
1995 }
1996 }
1997 if (DeclaredLocks.empty())
1998 continue;
1999 CapabilityExpr Cp(Analyzer->SxBuilder.translate(Arg, nullptr),
2000 StringRef("mutex"), false);
2001 if (const auto *CBTE = dyn_cast(Arg->IgnoreCasts());
2003 if (auto Object = Analyzer->ConstructedObjects.find(CBTE->getSubExpr());
2004 Object != Analyzer->ConstructedObjects.end())
2006 }
2007 const FactEntry *Fact = FSet.findLock(Analyzer->FactMan, Cp);
2008 if (!Fact) {
2012 continue;
2013 }
2014 const auto *Scope = cast(Fact);
2015 for (const auto &[a, b] :
2016 zip_longest(DeclaredLocks, Scope->getUnderlyingMutexes())) {
2017 if (!a.has_value()) {
2018 Analyzer->Handler.handleExpectFewerUnderlyingMutexes(
2020 b.value().getKind(), b.value().toString());
2021 } else if (.has_value()) {
2022 Analyzer->Handler.handleExpectMoreUnderlyingMutexes(
2024 a.value().getKind(), a.value().toString());
2025 } else if (!a.value().equals(b.value())) {
2026 Analyzer->Handler.handleUnmatchedUnderlyingMutexes(
2028 a.value().getKind(), a.value().toString(), b.value().toString());
2029 break;
2030 }
2031 }
2032 }
2033 }
2034
2035
2036 bool Dtor = isa(D);
2037 for (const auto &M : ExclusiveLocksToRemove)
2039 for (const auto &M : SharedLocksToRemove)
2040 Analyzer->removeLock(FSet, M, Loc, Dtor, LK_Shared);
2041 for (const auto &M : GenericLocksToRemove)
2042 Analyzer->removeLock(FSet, M, Loc, Dtor, LK_Generic);
2043
2044
2045 FactEntry::SourceKind Source =
2046 !Scp.shouldIgnore() ? FactEntry::Managed : FactEntry::Acquired;
2047 for (const auto &M : ExclusiveLocksToAdd)
2048 Analyzer->addLock(FSet, std::make_unique(M, LK_Exclusive,
2049 Loc, Source));
2050 for (const auto &M : SharedLocksToAdd)
2051 Analyzer->addLock(
2052 FSet, std::make_unique(M, LK_Shared, Loc, Source));
2053
2055
2056 auto ScopedEntry = std::make_unique(
2057 Scp, Loc, FactEntry::Acquired);
2058 for (const auto &M : ExclusiveLocksToAdd)
2059 ScopedEntry->addLock(M);
2060 for (const auto &M : SharedLocksToAdd)
2061 ScopedEntry->addLock(M);
2062 for (const auto &M : ScopedReqsAndExcludes)
2063 ScopedEntry->addLock(M);
2064 for (const auto &M : ExclusiveLocksToRemove)
2065 ScopedEntry->addExclusiveUnlock(M);
2066 for (const auto &M : SharedLocksToRemove)
2067 ScopedEntry->addSharedUnlock(M);
2068 Analyzer->addLock(FSet, std::move(ScopedEntry));
2069 }
2070}
2071
2072
2073
2074
2075void BuildLockset::VisitUnaryOperator(const UnaryOperator *UO) {
2077 case UO_PostDec:
2078 case UO_PostInc:
2079 case UO_PreDec:
2080 case UO_PreInc:
2082 break;
2083 default:
2084 break;
2085 }
2086}
2087
2088
2089
2090
2091void BuildLockset::VisitBinaryOperator(const BinaryOperator *BO) {
2093 return;
2094
2095
2096 LVarCtx = Analyzer->LocalVarMap.getNextContext(CtxIndex, BO, LVarCtx);
2097
2099}
2100
2101
2102
2103
2104void BuildLockset::VisitCastExpr(const CastExpr *CE) {
2105 if (CE->getCastKind() != CK_LValueToRValue)
2106 return;
2108}
2109
2110void BuildLockset::examineArguments(const FunctionDecl *FD,
2113 bool SkipFirstParam) {
2114
2115 if (!FD)
2116 return;
2117
2118
2119
2120
2121
2122
2123 if (FD->hasAttr())
2124 return;
2125
2127 auto Param = Params.begin();
2128 if (SkipFirstParam)
2129 ++Param;
2130
2131
2132 for (auto Arg = ArgBegin; Param != Params.end() && Arg != ArgEnd;
2133 ++Param, ++Arg) {
2134 QualType Qt = (*Param)->getType();
2137 }
2138}
2139
2140void BuildLockset::VisitCallExpr(const CallExpr *Exp) {
2141 if (const auto *CE = dyn_cast(Exp)) {
2142 const auto *ME = dyn_cast(CE->getCallee());
2143
2145
2146 if (ME && MD) {
2147 if (ME->isArrow()) {
2148
2149 checkPtAccess(CE->getImplicitObjectArgument(), AK_Read);
2150 } else {
2151
2152 checkAccess(CE->getImplicitObjectArgument(), AK_Read);
2153 }
2154 }
2155
2156 examineArguments(CE->getDirectCallee(), CE->arg_begin(), CE->arg_end());
2157 } else if (const auto *OE = dyn_cast(Exp)) {
2159 switch (OEop) {
2160 case OO_Equal:
2161 case OO_PlusEqual:
2162 case OO_MinusEqual:
2163 case OO_StarEqual:
2164 case OO_SlashEqual:
2165 case OO_PercentEqual:
2166 case OO_CaretEqual:
2167 case OO_AmpEqual:
2168 case OO_PipeEqual:
2169 case OO_LessLessEqual:
2170 case OO_GreaterGreaterEqual:
2171 checkAccess(OE->getArg(1), AK_Read);
2172 [[fallthrough]];
2173 case OO_PlusPlus:
2174 case OO_MinusMinus:
2175 checkAccess(OE->getArg(0), AK_Written);
2176 break;
2177 case OO_Star:
2178 case OO_ArrowStar:
2179 case OO_Arrow:
2180 case OO_Subscript:
2181 if (!(OEop == OO_Star && OE->getNumArgs() > 1)) {
2182
2183 checkPtAccess(OE->getArg(0), AK_Read);
2184 }
2185 [[fallthrough]];
2186 default: {
2187
2188 const Expr *Obj = OE->getArg(0);
2189 checkAccess(Obj, AK_Read);
2190
2191
2192
2193 const FunctionDecl *FD = OE->getDirectCallee();
2194 examineArguments(FD, std::next(OE->arg_begin()), OE->arg_end(),
2195 !isa(FD));
2196 break;
2197 }
2198 }
2199 } else {
2201 }
2202
2203 auto *D = dyn_cast_or_null(Exp->getCalleeDecl());
2204 if ()
2205 return;
2206 handleCall(Exp, D);
2207}
2208
2209void BuildLockset::VisitCXXConstructExpr(const CXXConstructExpr *Exp) {
2211 if (D && D->isCopyConstructor()) {
2212 const Expr* Source = Exp->getArg(0);
2213 checkAccess(Source, AK_Read);
2214 } else {
2216 }
2218 handleCall(Exp, D);
2219}
2220
2222 if (auto *CE = dyn_cast(E))
2225 if (auto *CE = dyn_cast(E))
2226 if (CE->getCastKind() == CK_ConstructorConversion ||
2227 CE->getCastKind() == CK_UserDefinedConversion)
2229 if (auto *BTE = dyn_cast(E))
2230 E = BTE->getSubExpr();
2231 return E;
2232}
2233
2234void BuildLockset::VisitDeclStmt(const DeclStmt *S) {
2235
2236 LVarCtx = Analyzer->LocalVarMap.getNextContext(CtxIndex, S, LVarCtx);
2237
2238 for (auto *D : S->getDeclGroup()) {
2239 if (auto *VD = dyn_cast_or_null(D)) {
2240 const Expr *E = VD->getInit();
2241 if ()
2242 continue;
2244
2245
2246 if (auto *EWC = dyn_cast(E))
2249
2250 if (auto Object = Analyzer->ConstructedObjects.find(E);
2251 Object != Analyzer->ConstructedObjects.end()) {
2252 Object->second->setClangDecl(VD);
2253 Analyzer->ConstructedObjects.erase(Object);
2254 }
2255 }
2256 }
2257}
2258
2259void BuildLockset::VisitMaterializeTemporaryExpr(
2262 if (auto Object = Analyzer->ConstructedObjects.find(
2264 Object != Analyzer->ConstructedObjects.end()) {
2265 Object->second->setClangDecl(ExtD);
2266 Analyzer->ConstructedObjects.erase(Object);
2267 }
2268 }
2269}
2270
2271void BuildLockset::VisitReturnStmt(const ReturnStmt *S) {
2272 if (Analyzer->CurrentFunction == nullptr)
2273 return;
2274 const Expr *RetVal = S->getRetValue();
2275 if (!RetVal)
2276 return;
2277
2278
2279
2281 Analyzer->CurrentFunction->getReturnType().getCanonicalType();
2283 Analyzer->checkAccess(
2284 FunctionExitFSet, RetVal,
2287 }
2288}
2289
2290
2291
2292
2293
2294
2295bool ThreadSafetyAnalyzer::join(const FactEntry &A, const FactEntry &B,
2296 bool CanModify) {
2297 if (A.kind() != B.kind()) {
2298
2299
2300 if ((A.managed() || A.asserted()) && (B.managed() || B.asserted())) {
2301
2302 bool ShouldTakeB = B.kind() == LK_Shared;
2303 if (CanModify || !ShouldTakeB)
2304 return ShouldTakeB;
2305 }
2307 A.loc());
2308
2309 return CanModify && B.kind() == LK_Exclusive;
2310 } else {
2311
2312 return CanModify && A.asserted() && !B.asserted();
2313 }
2314}
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330void ThreadSafetyAnalyzer::intersectAndWarn(FactSet &EntrySet,
2331 const FactSet &ExitSet,
2335 FactSet EntrySetOrig = EntrySet;
2336
2337
2338 for (const auto &Fact : ExitSet) {
2339 const FactEntry &ExitFact = FactMan[Fact];
2340
2341 FactSet::iterator EntryIt = EntrySet.findLockIter(FactMan, ExitFact);
2342 if (EntryIt != EntrySet.end()) {
2343 if (join(FactMan[*EntryIt], ExitFact,
2345 *EntryIt = Fact;
2347 ExitFact.handleRemovalFromIntersection(ExitSet, FactMan, JoinLoc,
2348 EntryLEK, Handler);
2349 }
2350 }
2351
2352
2353 for (const auto &Fact : EntrySetOrig) {
2354 const FactEntry *EntryFact = &FactMan[Fact];
2355 const FactEntry *ExitFact = ExitSet.findLock(FactMan, *EntryFact);
2356
2357 if (!ExitFact) {
2360 EntryFact->handleRemovalFromIntersection(EntrySetOrig, FactMan, JoinLoc,
2361 ExitLEK, Handler);
2363 EntrySet.removeLock(FactMan, *EntryFact);
2364 }
2365 }
2366}
2367
2368
2371 return true;
2373 return false;
2374
2376 if (std::optional S = Last.getAs<CFGStmt>()) {
2377 if (isa(S->getStmt()))
2378 return true;
2379 }
2380 return false;
2381}
2382
2383
2384
2385
2386
2387
2389
2390
2392 if (!walker.init(AC))
2393 return;
2394
2395
2396
2397
2400 CurrentFunction = dyn_cast(D);
2401
2403 return;
2404
2405
2406
2407
2408
2409 if (isa(D))
2410 return;
2411 if (isa(D))
2412 return;
2413
2415
2417 CFGBlockInfo::getEmptyBlockInfo(LocalVarMap));
2418
2419
2420
2421
2424
2425 CFGBlockInfo &Initial = BlockInfo[CFGraph->getEntry().getBlockID()];
2426 CFGBlockInfo &Final = BlockInfo[CFGraph->getExit().getBlockID()];
2427
2428
2429 Initial.Reachable = true;
2430
2431
2432 LocalVarMap.traverseCFG(CFGraph, SortedGraph, BlockInfo);
2433
2434
2436
2437 CapExprSet ExclusiveLocksAcquired;
2438 CapExprSet SharedLocksAcquired;
2439 CapExprSet LocksReleased;
2440
2441
2442
2443
2444 if (!SortedGraph->empty()) {
2445 assert(*SortedGraph->begin() == &CFGraph->getEntry());
2446 FactSet &InitialLockset = Initial.EntrySet;
2447
2448 CapExprSet ExclusiveLocksToAdd;
2449 CapExprSet SharedLocksToAdd;
2450
2452 for (const auto *Attr : D->attrs()) {
2454 if (const auto *A = dyn_cast(Attr)) {
2455 getMutexIDs(A->isShared() ? SharedLocksToAdd : ExclusiveLocksToAdd, A,
2456 nullptr, D);
2457 } else if (const auto *A = dyn_cast(Attr)) {
2458
2459
2460 if (A->args_size() == 0)
2461 return;
2462 getMutexIDs(A->isShared() ? SharedLocksToAdd : ExclusiveLocksToAdd, A,
2463 nullptr, D);
2464 getMutexIDs(LocksReleased, A, nullptr, D);
2465 } else if (const auto *A = dyn_cast(Attr)) {
2466 if (A->args_size() == 0)
2467 return;
2468 getMutexIDs(A->isShared() ? SharedLocksAcquired
2469 : ExclusiveLocksAcquired,
2470 A, nullptr, D);
2471 } else if (isa(Attr)) {
2472
2473 return;
2474 } else if (isa(Attr)) {
2475
2476 return;
2477 } else if (isa(Attr)) {
2478
2479 return;
2480 }
2481 }
2483 if (CurrentFunction)
2485 else if (auto CurrentMethod = dyn_cast(D))
2486 Params = CurrentMethod->getCanonicalDecl()->parameters();
2487 else
2488 llvm_unreachable("Unknown function kind");
2489 for (const ParmVarDecl *Param : Params) {
2490 CapExprSet UnderlyingLocks;
2491 for (const auto *Attr : Param->attrs()) {
2493 if (const auto *A = dyn_cast(Attr)) {
2494 getMutexIDs(A->isShared() ? SharedLocksToAdd : ExclusiveLocksToAdd, A,
2495 nullptr, Param);
2496 getMutexIDs(LocksReleased, A, nullptr, Param);
2497 getMutexIDs(UnderlyingLocks, A, nullptr, Param);
2498 } else if (const auto *A = dyn_cast(Attr)) {
2499 getMutexIDs(A->isShared() ? SharedLocksToAdd : ExclusiveLocksToAdd, A,
2500 nullptr, Param);
2501 getMutexIDs(UnderlyingLocks, A, nullptr, Param);
2502 } else if (const auto *A = dyn_cast(Attr)) {
2503 getMutexIDs(A->isShared() ? SharedLocksAcquired
2504 : ExclusiveLocksAcquired,
2505 A, nullptr, Param);
2506 getMutexIDs(UnderlyingLocks, A, nullptr, Param);
2507 } else if (const auto *A = dyn_cast(Attr)) {
2508 getMutexIDs(UnderlyingLocks, A, nullptr, Param);
2509 }
2510 }
2511 if (UnderlyingLocks.empty())
2512 continue;
2513 CapabilityExpr Cp(SxBuilder.createVariable(Param), StringRef(), false);
2514 auto ScopedEntry = std::make_unique(
2515 Cp, Param->getLocation(), FactEntry::Declared);
2517 ScopedEntry->addLock(M);
2518 addLock(InitialLockset, std::move(ScopedEntry), true);
2519 }
2520
2521
2522 for (const auto &Mu : ExclusiveLocksToAdd) {
2523 auto Entry = std::make_unique(Mu, LK_Exclusive, Loc,
2524 FactEntry::Declared);
2525 addLock(InitialLockset, std::move(Entry), true);
2526 }
2527 for (const auto &Mu : SharedLocksToAdd) {
2528 auto Entry = std::make_unique(Mu, LK_Shared, Loc,
2529 FactEntry::Declared);
2530 addLock(InitialLockset, std::move(Entry), true);
2531 }
2532 }
2533
2534
2535
2536 FactSet ExpectedFunctionExitSet = Initial.EntrySet;
2537
2538
2539
2540
2541
2542 for (const auto &Lock : ExclusiveLocksAcquired)
2543 ExpectedFunctionExitSet.addLock(
2544 FactMan, std::make_unique(Lock, LK_Exclusive,
2546 for (const auto &Lock : SharedLocksAcquired)
2547 ExpectedFunctionExitSet.addLock(
2548 FactMan,
2550 for (const auto &Lock : LocksReleased)
2551 ExpectedFunctionExitSet.removeLock(FactMan, Lock);
2552
2553 for (const auto *CurrBlock : *SortedGraph) {
2554 unsigned CurrBlockID = CurrBlock->getBlockID();
2555 CFGBlockInfo *CurrBlockInfo = &BlockInfo[CurrBlockID];
2556
2557
2558 VisitedBlocks.insert(CurrBlock);
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573 bool LocksetInitialized = false;
2575 PE = CurrBlock->pred_end(); PI != PE; ++PI) {
2576
2577 if (*PI == nullptr || !VisitedBlocks.alreadySet(*PI))
2578 continue;
2579
2580 unsigned PrevBlockID = (*PI)->getBlockID();
2581 CFGBlockInfo *PrevBlockInfo = &BlockInfo[PrevBlockID];
2582
2583
2584 if (neverReturns(*PI) || !PrevBlockInfo->Reachable)
2585 continue;
2586
2587
2588 CurrBlockInfo->Reachable = true;
2589
2590 FactSet PrevLockset;
2591 getEdgeLockset(PrevLockset, PrevBlockInfo->ExitSet, *PI, CurrBlock);
2592
2593 if (!LocksetInitialized) {
2594 CurrBlockInfo->EntrySet = PrevLockset;
2595 LocksetInitialized = true;
2596 } else {
2597
2598
2599
2600 intersectAndWarn(
2601 CurrBlockInfo->EntrySet, PrevLockset, CurrBlockInfo->EntryLoc,
2602 isa_and_nonnull((*PI)->getTerminatorStmt())
2605 }
2606 }
2607
2608
2609 if (!CurrBlockInfo->Reachable)
2610 continue;
2611
2612 BuildLockset LocksetBuilder(this, *CurrBlockInfo, ExpectedFunctionExitSet);
2613
2614
2615 for (const auto &BI : *CurrBlock) {
2616 switch (BI.getKind()) {
2619 LocksetBuilder.Visit(CS.getStmt());
2620 break;
2621 }
2622
2626 if (!DD->hasAttrs())
2627 break;
2628
2629 LocksetBuilder.handleCall(nullptr, DD,
2630 SxBuilder.createVariable(AD.getVarDecl()),
2632 break;
2633 }
2634
2637 LocksetBuilder.handleCall(nullptr, CF.getFunctionDecl(),
2638 SxBuilder.createVariable(CF.getVarDecl()),
2639 CF.getVarDecl()->getLocation());
2640 break;
2641 }
2642
2645
2646
2647
2648 if (auto Object = ConstructedObjects.find(
2649 TD.getBindTemporaryExpr()->getSubExpr());
2650 Object != ConstructedObjects.end()) {
2652 if (DD->hasAttrs())
2653
2654 LocksetBuilder.handleCall(nullptr, DD, Object->second,
2655 TD.getBindTemporaryExpr()->getEndLoc());
2656 ConstructedObjects.erase(Object);
2657 }
2658 break;
2659 }
2660 default:
2661 break;
2662 }
2663 }
2664 CurrBlockInfo->ExitSet = LocksetBuilder.FSet;
2665
2666
2667
2668
2669
2671 SE = CurrBlock->succ_end(); SI != SE; ++SI) {
2672
2673 if (*SI == nullptr || !VisitedBlocks.alreadySet(*SI))
2674 continue;
2675
2676 CFGBlock *FirstLoopBlock = *SI;
2677 CFGBlockInfo *PreLoop = &BlockInfo[FirstLoopBlock->getBlockID()];
2678 CFGBlockInfo *LoopEnd = &BlockInfo[CurrBlockID];
2679 intersectAndWarn(PreLoop->EntrySet, LoopEnd->ExitSet, PreLoop->EntryLoc,
2681 }
2682 }
2683
2684
2685 if (!Final.Reachable)
2686 return;
2687
2688
2689 intersectAndWarn(ExpectedFunctionExitSet, Final.ExitSet, Final.ExitLoc,
2691
2693}
2694
2695
2696
2697
2698
2699
2703 if (!*BSet)
2705 ThreadSafetyAnalyzer Analyzer(Handler, *BSet);
2706 Analyzer.runAnalysis(AC);
2707}
2708
2710
2711
2712
2714 switch (AK) {
2719 }
2720 llvm_unreachable("Unknown AccessKind");
2721}
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::@1726::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
FunctionDecl * getCanonicalDecl() override
Retrieves the "canonical" declaration of the given declaration.
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.
Represents a parameter to a function.
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;.
Scope - A scope is a transient data structure that is used while parsing the program.
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'.
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)
- 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.
OverloadedOperatorKind
Enumeration specifying the different kinds of C++ overloaded operators.
@ Self
'self' clause, allowed on Compute and Combined Constructs, plus 'update'.
@ Result
The result type of a method or function.
const FunctionProtoType * T
Iterator for iterating over Stmt * arrays that contain only T *.