}
-// Various helper functions on til::SExpr
-namespace sx {
-
-bool isUniversal(const til::SExpr *E) {
- return isa<til::Wildcard>(E);
-}
-
-bool equals(const til::SExpr *E1, const til::SExpr *E2) {
- return til::EqualsComparator::compareExprs(E1, E2);
-}
-
-const til::SExpr* ignorePtrCasts(const til::SExpr *E) {
- if (auto *CE = dyn_cast<til::Cast>(E)) {
- if (CE->castOpcode() == til::CAST_objToPtr)
- return CE->expr();
- }
- return E;
-}
-
-bool matches(const til::SExpr *E1, const til::SExpr *E2) {
- // We treat a top-level wildcard as the "univsersal" lock.
- // It matches everything for the purpose of checking locks, but not
- // for unlocking them.
- if (isa<til::Wildcard>(E1))
- return isa<til::Wildcard>(E2);
- if (isa<til::Wildcard>(E2))
- return isa<til::Wildcard>(E1);
-
- return til::MatchComparator::compareExprs(E1, E2);
-}
-
-bool partiallyMatches(const til::SExpr *E1, const til::SExpr *E2) {
- auto *PE1 = dyn_cast_or_null<til::Project>(E1);
- if (!PE1)
- return false;
- auto *PE2 = dyn_cast_or_null<til::Project>(E2);
- if (!PE2)
- return false;
- return PE1->clangDecl() == PE2->clangDecl();
-}
-
-std::string toString(const til::SExpr *E) {
- std::stringstream ss;
- til::StdPrinter::print(E, ss);
- return ss.str();
-}
-
-bool shouldIgnore(const til::SExpr *E) {
- if (!E)
- return true;
- // Trap mutex expressions like nullptr, or 0.
- // Any literal value is nonsense.
- if (isa<til::Literal>(E))
- return true;
- return false;
-}
-
-} // end namespace sx
-
-
-
-/// \brief A short list of SExprs
-class MutexIDList : public SmallVector<const til::SExpr*, 3> {
+/// \brief A set of CapabilityInfo objects, which are compiled from the
+/// requires attributes on a function.
+class CapExprSet : public SmallVector<CapabilityExpr, 4> {
public:
/// \brief Push M onto list, but discard duplicates.
- void push_back_nodup(const til::SExpr *E) {
- iterator It = std::find_if(begin(), end(), [=](const til::SExpr *E2) {
- return sx::equals(E, E2);
+ void push_back_nodup(const CapabilityExpr &CapE) {
+ iterator It = std::find_if(begin(), end(),
+ [=](const CapabilityExpr &CapE2) {
+ return CapE.equals(CapE2);
});
if (It == end())
- push_back(E);
+ push_back(CapE);
}
};
-/// \brief This is a helper class that stores info about the most recent
-/// accquire of a Lock.
+
+
+/// \brief This is a helper class that stores a fact that is known at a
+/// particular point in program execution. Currently, a fact is a capability,
+/// along with additional information, such as where it was acquired, whether
+/// it is exclusive or shared, etc.
///
-/// The main body of the analysis maps MutexIDs to LockDatas.
-struct LockData {
- SourceLocation AcquireLoc;
-
- /// \brief LKind stores whether a lock is held shared or exclusively.
- /// Note that this analysis does not currently support either re-entrant
- /// locking or lock "upgrading" and "downgrading" between exclusive and
- /// shared.
- ///
- /// FIXME: add support for re-entrant locking and lock up/downgrading
- LockKind LKind;
- bool Asserted; // for asserted locks
- bool Managed; // for ScopedLockable objects
- const til::SExpr* UnderlyingMutex; // for ScopedLockable objects
-
- LockData(SourceLocation AcquireLoc, LockKind LKind, bool M=false,
- bool Asrt=false)
- : AcquireLoc(AcquireLoc), LKind(LKind), Asserted(Asrt), Managed(M),
- UnderlyingMutex(nullptr)
- {}
+/// FIXME: this analysis does not currently support either re-entrant
+/// locking or lock "upgrading" and "downgrading" between exclusive and
+/// shared.
+class FactEntry : public CapabilityExpr {
+private:
+ LockKind LKind; //< exclusive or shared
+ SourceLocation AcquireLoc; //< where it was acquired.
+ bool Managed; //< for ScopedLockable objects
+ bool Asserted; //< true if the lock was asserted
+ const til::SExpr* UnderlyingMutex; //< for ScopedLockable objects
- LockData(SourceLocation AcquireLoc, LockKind LKind, const til::SExpr *Mu)
- : AcquireLoc(AcquireLoc), LKind(LKind), Asserted(false), Managed(false),
- UnderlyingMutex(Mu)
+public:
+ FactEntry(const CapabilityExpr &CE, LockKind LK, SourceLocation Loc,
+ bool Mng=false, bool Asrt=false)
+ : CapabilityExpr(CE), LKind(LK), AcquireLoc(Loc), Managed(Mng),
+ Asserted(Asrt), UnderlyingMutex(nullptr)
{}
- bool operator==(const LockData &other) const {
- return AcquireLoc == other.AcquireLoc && LKind == other.LKind;
- }
-
- bool operator!=(const LockData &other) const {
- return !(*this == other);
- }
+ FactEntry(const CapabilityExpr &CE, LockKind LK, SourceLocation Loc,
+ const til::SExpr *Mu)
+ : CapabilityExpr(CE), LKind(LK), AcquireLoc(Loc), Managed(false),
+ Asserted(false), UnderlyingMutex(Mu)
+ {}
- void Profile(llvm::FoldingSetNodeID &ID) const {
- ID.AddInteger(AcquireLoc.getRawEncoding());
- ID.AddInteger(LKind);
- }
+ LockKind kind() const { return LKind; }
+ SourceLocation loc() const { return AcquireLoc; }
+ bool asserted() const { return Asserted; }
+ bool managed() const { return Managed; }
+ const til::SExpr* underlying() const { return UnderlyingMutex; }
+ // Return true if LKind >= LK, where exclusive > shared
bool isAtLeast(LockKind LK) {
- return (LK == LK_Shared) || (LKind == LK_Exclusive);
+ return (LKind == LK_Exclusive) || (LK == LK_Shared);
}
};
-/// \brief A FactEntry stores a single fact that is known at a particular point
-/// in the program execution. Currently, this is information regarding a lock
-/// that is held at that point.
-struct FactEntry {
- const til::SExpr *MutID;
- LockData LDat;
-
- FactEntry(const til::SExpr* M, const LockData& L)
- : MutID(M), LDat(L)
- { }
-};
-
-
typedef unsigned short FactID;
/// \brief FactManager manages the memory for all facts that are created during
std::vector<FactEntry> Facts;
public:
- FactID newLock(const til::SExpr *M, const LockData& L) {
- Facts.push_back(FactEntry(M, L));
+ FactID newFact(const FactEntry &Entry) {
+ Facts.push_back(Entry);
return static_cast<unsigned short>(Facts.size() - 1);
}
bool isEmpty() const { return FactIDs.size() == 0; }
- FactID addLock(FactManager& FM, const til::SExpr *M, const LockData& L) {
- FactID F = FM.newLock(M, L);
+ FactID addLock(FactManager& FM, const FactEntry &Entry) {
+ FactID F = FM.newFact(Entry);
FactIDs.push_back(F);
return F;
}
- bool removeLock(FactManager& FM, const til::SExpr *M) {
+ bool removeLock(FactManager& FM, const CapabilityExpr &CapE) {
unsigned n = FactIDs.size();
if (n == 0)
return false;
for (unsigned i = 0; i < n-1; ++i) {
- if (sx::matches(FM[FactIDs[i]].MutID, M)) {
+ if (FM[FactIDs[i]].matches(CapE)) {
FactIDs[i] = FactIDs[n-1];
FactIDs.pop_back();
return true;
}
}
- if (sx::matches(FM[FactIDs[n-1]].MutID, M)) {
+ if (FM[FactIDs[n-1]].matches(CapE)) {
FactIDs.pop_back();
return true;
}
return false;
}
- iterator findLockIter(FactManager &FM, const til::SExpr *M) {
+ iterator findLockIter(FactManager &FM, const CapabilityExpr &CapE) {
return std::find_if(begin(), end(), [&](FactID ID) {
- return sx::matches(FM[ID].MutID, M);
+ return FM[ID].matches(CapE);
});
}
- LockData *findLock(FactManager &FM, const til::SExpr *M) const {
+ FactEntry *findLock(FactManager &FM, const CapabilityExpr &CapE) const {
auto I = std::find_if(begin(), end(), [&](FactID ID) {
- return sx::matches(FM[ID].MutID, M);
+ return FM[ID].matches(CapE);
});
-
- return I != end() ? &FM[*I].LDat : nullptr;
+ return I != end() ? &FM[*I] : nullptr;
}
- LockData *findLockUniv(FactManager &FM, const til::SExpr *M) const {
+ FactEntry *findLockUniv(FactManager &FM, const CapabilityExpr &CapE) const {
auto I = std::find_if(begin(), end(), [&](FactID ID) -> bool {
- const til::SExpr *E = FM[ID].MutID;
- return sx::isUniversal(E) || sx::matches(E, M);
+ return FM[ID].matchesUniv(CapE);
});
-
- return I != end() ? &FM[*I].LDat : nullptr;
+ return I != end() ? &FM[*I] : nullptr;
}
- FactEntry *findPartialMatch(FactManager &FM, const til::SExpr *M) const {
+ FactEntry *findPartialMatch(FactManager &FM,
+ const CapabilityExpr &CapE) const {
auto I = std::find_if(begin(), end(), [&](FactID ID) {
- return sx::partiallyMatches(FM[ID].MutID, M);
+ return FM[ID].partiallyMatches(CapE);
});
-
return I != end() ? &FM[*I] : nullptr;
}
};
-/// A Lockset maps each SExpr (defined above) to information about how it has
-/// been locked.
-typedef llvm::ImmutableMap<til::SExpr*, LockData> Lockset;
typedef llvm::ImmutableMap<const NamedDecl*, unsigned> LocalVarContext;
-
class LocalVariableMap;
/// A side (entry or exit) of a CFG node.
threadSafety::SExprBuilder SxBuilder;
ThreadSafetyHandler &Handler;
+ const CXXMethodDecl *CurrentMethod;
LocalVariableMap LocalVarMap;
FactManager FactMan;
std::vector<CFGBlockInfo> BlockInfo;
ThreadSafetyAnalyzer(ThreadSafetyHandler &H)
: Arena(&Bpa), SxBuilder(Arena), Handler(H) {}
- void addLock(FactSet &FSet, const til::SExpr *Mutex, const LockData &LDat,
- StringRef DiagKind);
- void removeLock(FactSet &FSet, const til::SExpr *Mutex,
+ void addLock(FactSet &FSet, const FactEntry &Entry, StringRef DiagKind);
+ void removeLock(FactSet &FSet, const CapabilityExpr &CapE,
SourceLocation UnlockLoc, bool FullyRemove, LockKind Kind,
StringRef DiagKind);
template <typename AttrType>
- void getMutexIDs(MutexIDList &Mtxs, AttrType *Attr, Expr *Exp,
+ void getMutexIDs(CapExprSet &Mtxs, AttrType *Attr, Expr *Exp,
const NamedDecl *D, VarDecl *SelfDecl = nullptr);
template <class AttrType>
- void getMutexIDs(MutexIDList &Mtxs, AttrType *Attr, Expr *Exp,
+ void getMutexIDs(CapExprSet &Mtxs, AttrType *Attr, Expr *Exp,
const NamedDecl *D,
const CFGBlock *PredBlock, const CFGBlock *CurrBlock,
Expr *BrE, bool Neg);
/// \brief Add a new lock to the lockset, warning if the lock is already there.
/// \param Mutex -- the Mutex expression for the lock
/// \param LDat -- the LockData for the lock
-void ThreadSafetyAnalyzer::addLock(FactSet &FSet, const til::SExpr *Mutex,
- const LockData &LDat, StringRef DiagKind) {
- // FIXME: deal with acquired before/after annotations.
- // FIXME: Don't always warn when we have support for reentrant locks.
- if (sx::shouldIgnore(Mutex))
+void ThreadSafetyAnalyzer::addLock(FactSet &FSet, const FactEntry &Entry,
+ StringRef DiagKind) {
+ if (Entry.shouldIgnore())
return;
- if (FSet.findLock(FactMan, Mutex)) {
- if (!LDat.Asserted)
- Handler.handleDoubleLock(DiagKind, sx::toString(Mutex), LDat.AcquireLoc);
+
+
+ // FIXME: deal with acquired before/after annotations.
+ // FIXME: Don't always warn when we have support for reentrant locks.
+ if (!Entry.asserted() && FSet.findLock(FactMan, Entry)) {
+ Handler.handleDoubleLock(DiagKind, Entry.toString(), Entry.loc());
} else {
- FSet.addLock(FactMan, Mutex, LDat);
+ FSet.addLock(FactMan, Entry);
}
}
/// \brief Remove a lock from the lockset, warning if the lock is not there.
/// \param Mutex The lock expression corresponding to the lock to be removed
/// \param UnlockLoc The source location of the unlock (only used in error msg)
-void ThreadSafetyAnalyzer::removeLock(FactSet &FSet, const til::SExpr *Mutex,
+void ThreadSafetyAnalyzer::removeLock(FactSet &FSet, const CapabilityExpr &Cp,
SourceLocation UnlockLoc,
bool FullyRemove, LockKind ReceivedKind,
StringRef DiagKind) {
- if (sx::shouldIgnore(Mutex))
+ if (Cp.shouldIgnore())
return;
- const LockData *LDat = FSet.findLock(FactMan, Mutex);
+ const FactEntry *LDat = FSet.findLock(FactMan, Cp);
if (!LDat) {
- Handler.handleUnmatchedUnlock(DiagKind, sx::toString(Mutex), UnlockLoc);
+ Handler.handleUnmatchedUnlock(DiagKind, Cp.toString(), UnlockLoc);
return;
}
// Generic lock removal doesn't care about lock kind mismatches, but
// otherwise diagnose when the lock kinds are mismatched.
- if (ReceivedKind != LK_Generic && LDat->LKind != ReceivedKind) {
- Handler.handleIncorrectUnlockKind(DiagKind, sx::toString(Mutex),
- LDat->LKind, ReceivedKind, UnlockLoc);
+ if (ReceivedKind != LK_Generic && LDat->kind() != ReceivedKind) {
+ Handler.handleIncorrectUnlockKind(DiagKind, Cp.toString(),
+ LDat->kind(), ReceivedKind, UnlockLoc);
return;
}
- if (LDat->UnderlyingMutex) {
+ if (LDat->underlying()) {
+ CapabilityExpr UnderCp(LDat->underlying(), false);
+
// This is scoped lockable object, which manages the real mutex.
if (FullyRemove) {
// We're destroying the managing object.
// Remove the underlying mutex if it exists; but don't warn.
- if (FSet.findLock(FactMan, LDat->UnderlyingMutex))
- FSet.removeLock(FactMan, LDat->UnderlyingMutex);
+ if (FSet.findLock(FactMan, UnderCp))
+ FSet.removeLock(FactMan, UnderCp);
} else {
// We're releasing the underlying mutex, but not destroying the
// managing object. Warn on dual release.
- if (!FSet.findLock(FactMan, LDat->UnderlyingMutex)) {
- Handler.handleUnmatchedUnlock(
- DiagKind, sx::toString(LDat->UnderlyingMutex), UnlockLoc);
+ if (!FSet.findLock(FactMan, UnderCp)) {
+ Handler.handleUnmatchedUnlock(DiagKind, UnderCp.toString(), UnlockLoc);
}
- FSet.removeLock(FactMan, LDat->UnderlyingMutex);
+ FSet.removeLock(FactMan, UnderCp);
return;
}
}
- FSet.removeLock(FactMan, Mutex);
+ FSet.removeLock(FactMan, Cp);
}
/// \brief Extract the list of mutexIDs from the attribute on an expression,
/// and push them onto Mtxs, discarding any duplicates.
template <typename AttrType>
-void ThreadSafetyAnalyzer::getMutexIDs(MutexIDList &Mtxs, AttrType *Attr,
+void ThreadSafetyAnalyzer::getMutexIDs(CapExprSet &Mtxs, AttrType *Attr,
Expr *Exp, const NamedDecl *D,
VarDecl *SelfDecl) {
if (Attr->args_size() == 0) {
// The mutex held is the "this" object.
- til::SExpr *Mu = SxBuilder.translateAttrExpr(nullptr, D, Exp, SelfDecl);
- if (Mu && isa<til::Undefined>(Mu)) {
+ CapabilityExpr Cp = SxBuilder.translateAttrExpr(nullptr, D, Exp, SelfDecl);
+ if (Cp.isInvalid()) {
warnInvalidLock(Handler, nullptr, D, Exp, ClassifyDiagnostic(Attr));
return;
}
//else
- if (Mu)
- Mtxs.push_back_nodup(Mu);
+ if (!Cp.shouldIgnore())
+ Mtxs.push_back_nodup(Cp);
return;
}
for (const auto *Arg : Attr->args()) {
- til::SExpr *Mu = SxBuilder.translateAttrExpr(Arg, D, Exp, SelfDecl);
- if (Mu && isa<til::Undefined>(Mu)) {
+ CapabilityExpr Cp = SxBuilder.translateAttrExpr(Arg, D, Exp, SelfDecl);
+ if (Cp.isInvalid()) {
warnInvalidLock(Handler, nullptr, D, Exp, ClassifyDiagnostic(Attr));
- return;
+ continue;
}
//else
- if (Mu)
- Mtxs.push_back_nodup(Mu);
+ if (!Cp.shouldIgnore())
+ Mtxs.push_back_nodup(Cp);
}
}
/// trylock applies to the given edge, then push them onto Mtxs, discarding
/// any duplicates.
template <class AttrType>
-void ThreadSafetyAnalyzer::getMutexIDs(MutexIDList &Mtxs, AttrType *Attr,
+void ThreadSafetyAnalyzer::getMutexIDs(CapExprSet &Mtxs, AttrType *Attr,
Expr *Exp, const NamedDecl *D,
const CFGBlock *PredBlock,
const CFGBlock *CurrBlock,
if(!FunDecl || !FunDecl->hasAttrs())
return;
- MutexIDList ExclusiveLocksToAdd;
- MutexIDList SharedLocksToAdd;
+ CapExprSet ExclusiveLocksToAdd;
+ CapExprSet SharedLocksToAdd;
// If the condition is a call to a Trylock function, then grab the attributes
for (auto *Attr : FunDecl->getAttrs()) {
// Add and remove locks.
SourceLocation Loc = Exp->getExprLoc();
for (const auto &ExclusiveLockToAdd : ExclusiveLocksToAdd)
- addLock(Result, ExclusiveLockToAdd, LockData(Loc, LK_Exclusive),
+ addLock(Result, FactEntry(ExclusiveLockToAdd, LK_Exclusive, Loc),
CapDiagKind);
for (const auto &SharedLockToAdd : SharedLocksToAdd)
- addLock(Result, SharedLockToAdd, LockData(Loc, LK_Shared), CapDiagKind);
+ addLock(Result, FactEntry(SharedLockToAdd, LK_Shared, Loc),
+ CapDiagKind);
}
/// \brief We use this class to visit different types of expressions in
unsigned CtxIndex;
// Helper functions
+ bool inCurrentScope(const CapabilityExpr &CapE);
void warnIfMutexNotHeld(const NamedDecl *D, const Expr *Exp, AccessKind AK,
Expr *MutexExp, ProtectedOperationKind POK,
void VisitDeclStmt(DeclStmt *S);
};
+
+inline bool BuildLockset::inCurrentScope(const CapabilityExpr &CapE) {
+ if (!Analyzer->CurrentMethod)
+ return false;
+ if (auto *P = dyn_cast_or_null<til::Project>(CapE.sexpr())) {
+ auto *VD = P->clangDecl();
+ if (VD)
+ return VD->getDeclContext() == Analyzer->CurrentMethod->getDeclContext();
+ }
+ return false;
+}
+
+
/// \brief Warn if the LSet does not contain a lock sufficient to protect access
/// of at least the passed in AccessKind.
void BuildLockset::warnIfMutexNotHeld(const NamedDecl *D, const Expr *Exp,
StringRef DiagKind) {
LockKind LK = getLockKindFromAccessKind(AK);
- til::SExpr *Mutex = Analyzer->SxBuilder.translateAttrExpr(MutexExp, D, Exp);
- if (!Mutex) {
- // TODO: invalid locks?
- // warnInvalidLock(Analyzer->Handler, MutexExp, D, Exp, DiagKind);
+ CapabilityExpr Cp = Analyzer->SxBuilder.translateAttrExpr(MutexExp, D, Exp);
+ if (Cp.isInvalid()) {
+ warnInvalidLock(Analyzer->Handler, MutexExp, D, Exp, DiagKind);
return;
- } else if (sx::shouldIgnore(Mutex)) {
+ } else if (Cp.shouldIgnore()) {
return;
}
- LockData* LDat = FSet.findLockUniv(Analyzer->FactMan, Mutex);
+ if (Cp.negative()) {
+ // Negative capabilities act like locks excluded
+ FactEntry *LDat = FSet.findLock(Analyzer->FactMan, !Cp);
+ if (LDat) {
+ Analyzer->Handler.handleFunExcludesLock(
+ DiagKind, D->getNameAsString(), (!Cp).toString(), Exp->getExprLoc());
+ return;
+ }
+
+ // If this does not refer to a negative capability in the same class,
+ // then stop here.
+ if (!inCurrentScope(Cp))
+ return;
+
+ // Otherwise the negative requirement must be propagated to the caller.
+ LDat = FSet.findLock(Analyzer->FactMan, Cp);
+ if (!LDat) {
+ Analyzer->Handler.handleMutexNotHeld("", D, POK, Cp.toString(),
+ LK_Shared, Exp->getExprLoc());
+ }
+ return;
+ }
+
+ FactEntry* LDat = FSet.findLockUniv(Analyzer->FactMan, Cp);
bool NoError = true;
if (!LDat) {
// No exact match found. Look for a partial match.
- FactEntry* FEntry = FSet.findPartialMatch(Analyzer->FactMan, Mutex);
- if (FEntry) {
+ LDat = FSet.findPartialMatch(Analyzer->FactMan, Cp);
+ if (LDat) {
// Warn that there's no precise match.
- LDat = &FEntry->LDat;
- std::string PartMatchStr = sx::toString(FEntry->MutID);
+ std::string PartMatchStr = LDat->toString();
StringRef PartMatchName(PartMatchStr);
Analyzer->Handler.handleMutexNotHeld(DiagKind, D, POK,
- sx::toString(Mutex),
+ Cp.toString(),
LK, Exp->getExprLoc(),
&PartMatchName);
} else {
// Warn that there's no match at all.
Analyzer->Handler.handleMutexNotHeld(DiagKind, D, POK,
- sx::toString(Mutex),
+ Cp.toString(),
LK, Exp->getExprLoc());
}
NoError = false;
}
// Make sure the mutex we found is the right kind.
- if (NoError && LDat && !LDat->isAtLeast(LK))
+ if (NoError && LDat && !LDat->isAtLeast(LK)) {
Analyzer->Handler.handleMutexNotHeld(DiagKind, D, POK,
- sx::toString(Mutex),
+ Cp.toString(),
LK, Exp->getExprLoc());
+ }
}
/// \brief Warn if the LSet contains the given lock.
void BuildLockset::warnIfMutexHeld(const NamedDecl *D, const Expr *Exp,
Expr *MutexExp,
StringRef DiagKind) {
- til::SExpr *Mutex = Analyzer->SxBuilder.translateAttrExpr(MutexExp, D, Exp);
- if (!Mutex) {
- // TODO: invalid locks?
- // warnInvalidLock(Analyzer->Handler, MutexExp, D, Exp, DiagKind);
+ CapabilityExpr Cp = Analyzer->SxBuilder.translateAttrExpr(MutexExp, D, Exp);
+ if (Cp.isInvalid()) {
+ warnInvalidLock(Analyzer->Handler, MutexExp, D, Exp, DiagKind);
+ return;
+ } else if (Cp.shouldIgnore()) {
return;
}
- LockData* LDat = FSet.findLock(Analyzer->FactMan, Mutex);
- if (LDat)
+ FactEntry* LDat = FSet.findLock(Analyzer->FactMan, Cp);
+ if (LDat) {
Analyzer->Handler.handleFunExcludesLock(
- DiagKind, D->getNameAsString(), sx::toString(Mutex), Exp->getExprLoc());
+ DiagKind, D->getNameAsString(), Cp.toString(), Exp->getExprLoc());
+ }
}
/// \brief Checks guarded_by and pt_guarded_by attributes.
void BuildLockset::handleCall(Expr *Exp, const NamedDecl *D, VarDecl *VD) {
SourceLocation Loc = Exp->getExprLoc();
const AttrVec &ArgAttrs = D->getAttrs();
- MutexIDList ExclusiveLocksToAdd, SharedLocksToAdd;
- MutexIDList ExclusiveLocksToRemove, SharedLocksToRemove, GenericLocksToRemove;
+ CapExprSet ExclusiveLocksToAdd, SharedLocksToAdd;
+ CapExprSet ExclusiveLocksToRemove, SharedLocksToRemove, GenericLocksToRemove;
StringRef CapDiagKind = "mutex";
for(unsigned i = 0; i < ArgAttrs.size(); ++i) {
case attr::AssertExclusiveLock: {
AssertExclusiveLockAttr *A = cast<AssertExclusiveLockAttr>(At);
- MutexIDList AssertLocks;
+ CapExprSet AssertLocks;
Analyzer->getMutexIDs(AssertLocks, A, Exp, D, VD);
for (const auto &AssertLock : AssertLocks)
- Analyzer->addLock(FSet, AssertLock,
- LockData(Loc, LK_Exclusive, false, true),
+ Analyzer->addLock(FSet, FactEntry(AssertLock, LK_Exclusive, Loc,
+ false, true),
ClassifyDiagnostic(A));
break;
}
case attr::AssertSharedLock: {
AssertSharedLockAttr *A = cast<AssertSharedLockAttr>(At);
- MutexIDList AssertLocks;
+ CapExprSet AssertLocks;
Analyzer->getMutexIDs(AssertLocks, A, Exp, D, VD);
for (const auto &AssertLock : AssertLocks)
- Analyzer->addLock(FSet, AssertLock,
- LockData(Loc, LK_Shared, false, true),
+ Analyzer->addLock(FSet, FactEntry(AssertLock, LK_Shared, Loc,
+ false, true),
ClassifyDiagnostic(A));
break;
}
// Add locks.
for (const auto &M : ExclusiveLocksToAdd)
- Analyzer->addLock(FSet, M, LockData(Loc, LK_Exclusive, isScopedVar),
+ Analyzer->addLock(FSet, FactEntry(M, LK_Exclusive, Loc, isScopedVar),
CapDiagKind);
for (const auto &M : SharedLocksToAdd)
- Analyzer->addLock(FSet, M, LockData(Loc, LK_Shared, isScopedVar),
+ Analyzer->addLock(FSet, FactEntry(M, LK_Shared, Loc, isScopedVar),
CapDiagKind);
// Add the managing object as a dummy mutex, mapped to the underlying mutex.
- // FIXME -- this doesn't work if we acquire multiple locks.
+ // FIXME: this doesn't work if we acquire multiple locks.
if (isScopedVar) {
SourceLocation MLoc = VD->getLocation();
DeclRefExpr DRE(VD, false, VD->getType(), VK_LValue, VD->getLocation());
- til::SExpr *SMutex = Analyzer->SxBuilder.translateAttrExpr(&DRE, nullptr);
+ // FIXME: does this store a pointer to DRE?
+ CapabilityExpr Scp = Analyzer->SxBuilder.translateAttrExpr(&DRE, nullptr);
for (const auto &M : ExclusiveLocksToAdd)
- Analyzer->addLock(FSet, SMutex, LockData(MLoc, LK_Exclusive, M),
+ Analyzer->addLock(FSet, FactEntry(Scp, LK_Exclusive, MLoc, M.sexpr()),
CapDiagKind);
for (const auto &M : SharedLocksToAdd)
- Analyzer->addLock(FSet, SMutex, LockData(MLoc, LK_Shared, M),
+ Analyzer->addLock(FSet, FactEntry(Scp, LK_Shared, MLoc, M.sexpr()),
CapDiagKind);
// handle corner case where the underlying mutex is invalid
if (ExclusiveLocksToAdd.size() == 0 && SharedLocksToAdd.size() == 0) {
- Analyzer->addLock(FSet, SMutex, LockData(MLoc, LK_Exclusive),
+ Analyzer->addLock(FSet, FactEntry(Scp, LK_Exclusive, MLoc),
CapDiagKind);
}
}
// Find locks in FSet2 that conflict or are not in FSet1, and warn.
for (const auto &Fact : FSet2) {
- const til::SExpr *FSet2Mutex = FactMan[Fact].MutID;
- const LockData &LDat2 = FactMan[Fact].LDat;
- FactSet::iterator I1 = FSet1.findLockIter(FactMan, FSet2Mutex);
-
- if (I1 != FSet1.end()) {
- const LockData* LDat1 = &FactMan[*I1].LDat;
- if (LDat1->LKind != LDat2.LKind) {
- Handler.handleExclusiveAndShared("mutex", sx::toString(FSet2Mutex),
- LDat2.AcquireLoc, LDat1->AcquireLoc);
- if (Modify && LDat1->LKind != LK_Exclusive) {
+ const FactEntry *LDat1 = nullptr;
+ const FactEntry *LDat2 = &FactMan[Fact];
+ FactSet::iterator Iter1 = FSet1.findLockIter(FactMan, *LDat2);
+ if (Iter1 != FSet1.end()) LDat1 = &FactMan[*Iter1];
+
+ if (LDat1) {
+ if (LDat1->kind() != LDat2->kind()) {
+ Handler.handleExclusiveAndShared("mutex", LDat2->toString(),
+ LDat2->loc(), LDat1->loc());
+ if (Modify && LDat1->kind() != LK_Exclusive) {
// Take the exclusive lock, which is the one in FSet2.
- *I1 = Fact;
+ *Iter1 = Fact;
}
}
- else if (LDat1->Asserted && !LDat2.Asserted) {
+ else if (Modify && LDat1->asserted() && !LDat2->asserted()) {
// The non-asserted lock in FSet2 is the one we want to track.
- *I1 = Fact;
+ *Iter1 = Fact;
}
} else {
- if (LDat2.UnderlyingMutex) {
- if (FSet2.findLock(FactMan, LDat2.UnderlyingMutex)) {
+ if (LDat2->underlying()) {
+ if (FSet2.findLock(FactMan, CapabilityExpr(LDat2->underlying(),
+ false))) {
// If this is a scoped lock that manages another mutex, and if the
// underlying mutex is still held, then warn about the underlying
// mutex.
Handler.handleMutexHeldEndOfScope("mutex",
- sx::toString(LDat2.UnderlyingMutex),
- LDat2.AcquireLoc, JoinLoc, LEK1);
+ sx::toString(LDat2->underlying()),
+ LDat2->loc(), JoinLoc, LEK1);
}
}
- else if (!LDat2.Managed && !sx::isUniversal(FSet2Mutex) &&
- !LDat2.Asserted)
- Handler.handleMutexHeldEndOfScope("mutex", sx::toString(FSet2Mutex),
- LDat2.AcquireLoc, JoinLoc, LEK1);
+ else if (!LDat2->managed() && !LDat2->asserted() &&
+ !LDat2->isUniversal()) {
+ Handler.handleMutexHeldEndOfScope("mutex", LDat2->toString(),
+ LDat2->loc(), JoinLoc, LEK1);
+ }
}
}
// Find locks in FSet1 that are not in FSet2, and remove them.
for (const auto &Fact : FSet1Orig) {
- const til::SExpr *FSet1Mutex = FactMan[Fact].MutID;
- const LockData &LDat1 = FactMan[Fact].LDat;
+ const FactEntry *LDat1 = &FactMan[Fact];
+ const FactEntry *LDat2 = FSet2.findLock(FactMan, *LDat1);
- if (!FSet2.findLock(FactMan, FSet1Mutex)) {
- if (LDat1.UnderlyingMutex) {
- if (FSet1Orig.findLock(FactMan, LDat1.UnderlyingMutex)) {
+ if (!LDat2) {
+ if (LDat1->underlying()) {
+ if (FSet1Orig.findLock(FactMan, CapabilityExpr(LDat1->underlying(),
+ false))) {
// If this is a scoped lock that manages another mutex, and if the
// underlying mutex is still held, then warn about the underlying
// mutex.
Handler.handleMutexHeldEndOfScope("mutex",
- sx::toString(LDat1.UnderlyingMutex),
- LDat1.AcquireLoc, JoinLoc, LEK1);
+ sx::toString(LDat1->underlying()),
+ LDat1->loc(), JoinLoc, LEK1);
}
}
- else if (!LDat1.Managed && !sx::isUniversal(FSet1Mutex) &&
- !LDat1.Asserted)
- Handler.handleMutexHeldEndOfScope("mutex", sx::toString(FSet1Mutex),
- LDat1.AcquireLoc, JoinLoc, LEK2);
+ else if (!LDat1->managed() && !LDat1->asserted() &&
+ !LDat1->isUniversal()) {
+ Handler.handleMutexHeldEndOfScope("mutex", LDat1->toString(),
+ LDat1->loc(), JoinLoc, LEK2);
+ }
if (Modify)
- FSet1.removeLock(FactMan, FSet1Mutex);
+ FSet1.removeLock(FactMan, *LDat1);
}
}
}
CFG *CFGraph = walker.getGraph();
const NamedDecl *D = walker.getDecl();
+ CurrentMethod = dyn_cast<CXXMethodDecl>(D);
if (D->hasAttr<NoThreadSafetyAnalysisAttr>())
return;
// Fill in source locations for all CFGBlocks.
findBlockLocations(CFGraph, SortedGraph, BlockInfo);
- MutexIDList ExclusiveLocksAcquired;
- MutexIDList SharedLocksAcquired;
- MutexIDList LocksReleased;
+ CapExprSet ExclusiveLocksAcquired;
+ CapExprSet SharedLocksAcquired;
+ CapExprSet LocksReleased;
// Add locks from exclusive_locks_required and shared_locks_required
// to initial lockset. Also turn off checking for lock and unlock functions.
FactSet &InitialLockset = BlockInfo[FirstBlock->getBlockID()].EntrySet;
const AttrVec &ArgAttrs = D->getAttrs();
- MutexIDList ExclusiveLocksToAdd;
- MutexIDList SharedLocksToAdd;
+ CapExprSet ExclusiveLocksToAdd;
+ CapExprSet SharedLocksToAdd;
StringRef CapDiagKind = "mutex";
SourceLocation Loc = D->getLocation();
}
// FIXME -- Loc can be wrong here.
- for (const auto &ExclusiveLockToAdd : ExclusiveLocksToAdd)
- addLock(InitialLockset, ExclusiveLockToAdd, LockData(Loc, LK_Exclusive),
+ for (const auto &Mu : ExclusiveLocksToAdd)
+ addLock(InitialLockset, FactEntry(Mu, LK_Exclusive, Loc),
CapDiagKind);
- for (const auto &SharedLockToAdd : SharedLocksToAdd)
- addLock(InitialLockset, SharedLockToAdd, LockData(Loc, LK_Shared),
+ for (const auto &Mu : SharedLocksToAdd)
+ addLock(InitialLockset, FactEntry(Mu, LK_Shared, Loc),
CapDiagKind);
}
// issue the appropriate warning.
// FIXME: the location here is not quite right.
for (const auto &Lock : ExclusiveLocksAcquired)
- ExpectedExitSet.addLock(FactMan, Lock,
- LockData(D->getLocation(), LK_Exclusive));
+ ExpectedExitSet.addLock(FactMan, FactEntry(Lock, LK_Exclusive,
+ D->getLocation()));
for (const auto &Lock : SharedLocksAcquired)
- ExpectedExitSet.addLock(FactMan, Lock,
- LockData(D->getLocation(), LK_Shared));
+ ExpectedExitSet.addLock(FactMan, FactEntry(Lock, LK_Shared,
+ D->getLocation()));
for (const auto &Lock : LocksReleased)
ExpectedExitSet.removeLock(FactMan, Lock);