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 (P)

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();

674 for (const auto &P : C)

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

1407 if (isa(E) || isa(E)) {

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(Self);

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,

1854 A, Exp, D, Self);

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,

1958 A, Exp, D, Self);

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 (b.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 (D)

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 (E)

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

2402 if (D->hasAttr())

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)

  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.

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 *.