[clang] Thread Safety Analysis: Support passing scoped locks between functions with appropriate annotations (PR #110523)
Aaron Puchert via cfe-commits
cfe-commits at lists.llvm.org
Mon Sep 30 15:47:30 PDT 2024
================
@@ -1915,6 +1936,101 @@ void BuildLockset::handleCall(const Expr *Exp, const NamedDecl *D,
}
}
+ std::optional<CallExpr::const_arg_range> Args;
+ if (Exp) {
+ if (const auto *CE = dyn_cast<CallExpr>(Exp))
+ Args = CE->arguments();
+ else if (const auto *CE = dyn_cast<CXXConstructExpr>(Exp))
+ Args = CE->arguments();
+ else
+ llvm_unreachable("Unknown call kind");
+ }
+ const FunctionDecl *CalledFunction = dyn_cast<FunctionDecl>(D);
+ if (CalledFunction && Args.has_value()) {
+ for (auto [Param, Arg] : zip(CalledFunction->parameters(), *Args)) {
+ CapExprSet DeclaredLocks;
+ for (const Attr *At : Param->attrs()) {
+ switch (At->getKind()) {
+ case attr::AcquireCapability: {
+ const auto *A = cast<AcquireCapabilityAttr>(At);
+ Analyzer->getMutexIDs(A->isShared() ? SharedLocksToAdd
+ : ExclusiveLocksToAdd,
+ A, Exp, D, Self);
+ Analyzer->getMutexIDs(DeclaredLocks, A, nullptr, Param);
+ break;
+ }
+
+ case attr::ReleaseCapability: {
+ const auto *A = cast<ReleaseCapabilityAttr>(At);
+ if (A->isGeneric())
+ Analyzer->getMutexIDs(GenericLocksToRemove, A, Exp, D, Self);
+ else if (A->isShared())
+ Analyzer->getMutexIDs(SharedLocksToRemove, A, Exp, D, Self);
+ else
+ Analyzer->getMutexIDs(ExclusiveLocksToRemove, A, Exp, D, Self);
+ Analyzer->getMutexIDs(DeclaredLocks, A, nullptr, Param);
+ break;
+ }
+
+ case attr::RequiresCapability: {
+ const auto *A = cast<RequiresCapabilityAttr>(At);
+ for (auto *Arg : A->args())
+ Analyzer->warnIfMutexNotHeld(FSet, D, Exp,
+ A->isShared() ? AK_Read : AK_Written,
+ Arg, POK_FunctionCall, Self, Loc);
+ Analyzer->getMutexIDs(DeclaredLocks, A, nullptr, Param);
+ break;
+ }
+
+ case attr::LocksExcluded: {
+ const auto *A = cast<LocksExcludedAttr>(At);
+ for (auto *Arg : A->args())
+ Analyzer->warnIfMutexHeld(FSet, D, Exp, Arg, Self, Loc);
+ Analyzer->getMutexIDs(DeclaredLocks, A, nullptr, Param);
+ break;
+ }
+
+ default:
+ break;
+ }
+ }
+ if (DeclaredLocks.size() == 0)
+ continue;
+ CapabilityExpr Cp = Analyzer->SxBuilder.translateAttrExpr(Arg, nullptr);
+ if (const auto *CBTE = dyn_cast<CXXBindTemporaryExpr>(Arg->IgnoreCasts());
+ Cp.isInvalid() && CBTE) {
+ if (auto Object = Analyzer->ConstructedObjects.find(CBTE->getSubExpr());
+ Object != Analyzer->ConstructedObjects.end())
+ Cp = CapabilityExpr(Object->second, StringRef(), false);
+ }
+ const FactEntry *Fact = FSet.findLock(Analyzer->FactMan, Cp);
+ if (!Fact) {
+ Analyzer->Handler.handleMutexNotHeld(Cp.getKind(), D, POK_FunctionCall,
+ Cp.toString(), LK_Exclusive,
+ Exp->getExprLoc());
+ continue;
+ }
+ const auto *Scope =
+ cast<ScopedLockableFactEntry>(Fact);
+ for (const auto &[a, b] :
+ zip_longest(DeclaredLocks, Scope->getUnderlyingMutexes())) {
+ if (!a.has_value()) {
+ Analyzer->Handler.handleExpectFewerUnderlyingMutexes(
+ Exp->getExprLoc(), D->getLocation(), Scope->toString(),
+ b.value().getKind(), b.value().toString());
+ } else if (!b.has_value()) {
+ Analyzer->Handler.handleExpectMoreUnderlyingMutexes(
+ Exp->getExprLoc(), D->getLocation(), Scope->toString(),
+ a.value().getKind(), a.value().toString());
+ } else if (!a.value().matches(b.value())) {
----------------
aaronpuchert wrote:
Should we use `equals` instead of `matches`? I don't think we can add wildcards to the set of known capabilities, they are only allowed on `guarded_by` or `requires` to indicate that any capability is sufficient.
https://github.com/llvm/llvm-project/pull/110523
More information about the cfe-commits
mailing list