aboutsummaryrefslogtreecommitdiff
path: root/clang/lib/Analysis/ThreadSafetyCommon.cpp
diff options
context:
space:
mode:
authorMarco Elver <elver@google.com>2025-05-26 17:03:55 +0200
committerGitHub <noreply@github.com>2025-05-26 17:03:55 +0200
commitc7ccfc6dfc1c2d0ca9cf5615f9f95bb7ad78b1c9 (patch)
tree79aef42f5a494cc7350c07d46c170a92b685668b /clang/lib/Analysis/ThreadSafetyCommon.cpp
parent365dcf48b8aa726fb6a9ace4b37eb1f1cf121941 (diff)
downloadllvm-c7ccfc6dfc1c2d0ca9cf5615f9f95bb7ad78b1c9.zip
llvm-c7ccfc6dfc1c2d0ca9cf5615f9f95bb7ad78b1c9.tar.gz
llvm-c7ccfc6dfc1c2d0ca9cf5615f9f95bb7ad78b1c9.tar.bz2
Thread Safety Analysis: Support reentrant capabilities (#137133)
Introduce the `reentrant_capability` attribute, which may be specified alongside the `capability(..)` attribute to denote that the defined capability type is reentrant. Marking a capability as reentrant means that acquiring the same capability multiple times is safe, and does not produce warnings on attempted re-acquisition. The most significant changes required are plumbing to propagate if the attribute is present to a CapabilityExpr, and introducing ReentrancyDepth to the LockableFactEntry class.
Diffstat (limited to 'clang/lib/Analysis/ThreadSafetyCommon.cpp')
-rw-r--r--clang/lib/Analysis/ThreadSafetyCommon.cpp76
1 files changed, 39 insertions, 37 deletions
diff --git a/clang/lib/Analysis/ThreadSafetyCommon.cpp b/clang/lib/Analysis/ThreadSafetyCommon.cpp
index 13cd7e2..d35ae94 100644
--- a/clang/lib/Analysis/ThreadSafetyCommon.cpp
+++ b/clang/lib/Analysis/ThreadSafetyCommon.cpp
@@ -67,6 +67,40 @@ static bool isIncompletePhi(const til::SExpr *E) {
return false;
}
+static constexpr std::pair<StringRef, bool> ClassifyCapabilityFallback{
+ /*Kind=*/StringRef("mutex"),
+ /*Reentrant=*/false};
+
+// Returns pair (Kind, Reentrant).
+static std::pair<StringRef, bool> classifyCapability(const TypeDecl &TD) {
+ if (const auto *CA = TD.getAttr<CapabilityAttr>())
+ return {CA->getName(), TD.hasAttr<ReentrantCapabilityAttr>()};
+
+ return ClassifyCapabilityFallback;
+}
+
+// Returns pair (Kind, Reentrant).
+static std::pair<StringRef, bool> classifyCapability(QualType QT) {
+ // We need to look at the declaration of the type of the value to determine
+ // which it is. The type should either be a record or a typedef, or a pointer
+ // or reference thereof.
+ if (const auto *RT = QT->getAs<RecordType>()) {
+ if (const auto *RD = RT->getDecl())
+ return classifyCapability(*RD);
+ } else if (const auto *TT = QT->getAs<TypedefType>()) {
+ if (const auto *TD = TT->getDecl())
+ return classifyCapability(*TD);
+ } else if (QT->isPointerOrReferenceType())
+ return classifyCapability(QT->getPointeeType());
+
+ return ClassifyCapabilityFallback;
+}
+
+CapabilityExpr::CapabilityExpr(const til::SExpr *E, QualType QT, bool Neg) {
+ const auto &[Kind, Reentrant] = classifyCapability(QT);
+ *this = CapabilityExpr(E, Kind, Neg, Reentrant);
+}
+
using CallingContext = SExprBuilder::CallingContext;
til::SExpr *SExprBuilder::lookupStmt(const Stmt *S) { return SMap.lookup(S); }
@@ -81,28 +115,6 @@ static bool isCalleeArrow(const Expr *E) {
return ME ? ME->isArrow() : false;
}
-static StringRef ClassifyDiagnostic(const CapabilityAttr *A) {
- return A->getName();
-}
-
-static StringRef ClassifyDiagnostic(QualType VDT) {
- // We need to look at the declaration of the type of the value to determine
- // which it is. The type should either be a record or a typedef, or a pointer
- // or reference thereof.
- if (const auto *RT = VDT->getAs<RecordType>()) {
- if (const auto *RD = RT->getDecl())
- if (const auto *CA = RD->getAttr<CapabilityAttr>())
- return ClassifyDiagnostic(CA);
- } else if (const auto *TT = VDT->getAs<TypedefType>()) {
- if (const auto *TD = TT->getDecl())
- if (const auto *CA = TD->getAttr<CapabilityAttr>())
- return ClassifyDiagnostic(CA);
- } else if (VDT->isPointerOrReferenceType())
- return ClassifyDiagnostic(VDT->getPointeeType());
-
- return "mutex";
-}
-
/// Translate a clang expression in an attribute to a til::SExpr.
/// Constructs the context from D, DeclExp, and SelfDecl.
///
@@ -170,9 +182,7 @@ CapabilityExpr SExprBuilder::translateAttrExpr(const Expr *AttrExp,
// If the attribute has no arguments, then assume the argument is "this".
if (!AttrExp)
return CapabilityExpr(
- Self,
- ClassifyDiagnostic(
- cast<CXXMethodDecl>(D)->getFunctionObjectParameterType()),
+ Self, cast<CXXMethodDecl>(D)->getFunctionObjectParameterType(),
false);
else // For most attributes.
return translateAttrExpr(AttrExp, &Ctx);
@@ -197,7 +207,7 @@ CapabilityExpr SExprBuilder::translateAttrExpr(const Expr *AttrExp,
// The "*" expr is a universal lock, which essentially turns off
// checks until it is removed from the lockset.
return CapabilityExpr(new (Arena) til::Wildcard(), StringRef("wildcard"),
- false);
+ /*Neg=*/false, /*Reentrant=*/false);
else
// Ignore other string literals for now.
return CapabilityExpr();
@@ -217,33 +227,25 @@ CapabilityExpr SExprBuilder::translateAttrExpr(const Expr *AttrExp,
}
}
- til::SExpr *E = translate(AttrExp, Ctx);
+ const til::SExpr *E = translate(AttrExp, Ctx);
// Trap mutex expressions like nullptr, or 0.
// Any literal value is nonsense.
if (!E || isa<til::Literal>(E))
return CapabilityExpr();
- StringRef Kind = ClassifyDiagnostic(AttrExp->getType());
-
// Hack to deal with smart pointers -- strip off top-level pointer casts.
if (const auto *CE = dyn_cast<til::Cast>(E)) {
if (CE->castOpcode() == til::CAST_objToPtr)
- return CapabilityExpr(CE->expr(), Kind, Neg);
+ E = CE->expr();
}
- return CapabilityExpr(E, Kind, Neg);
+ return CapabilityExpr(E, AttrExp->getType(), Neg);
}
til::LiteralPtr *SExprBuilder::createVariable(const VarDecl *VD) {
return new (Arena) til::LiteralPtr(VD);
}
-std::pair<til::LiteralPtr *, StringRef>
-SExprBuilder::createThisPlaceholder(const Expr *Exp) {
- return {new (Arena) til::LiteralPtr(nullptr),
- ClassifyDiagnostic(Exp->getType())};
-}
-
// Translate a clang statement or expression to a TIL expression.
// Also performs substitution of variables; Ctx provides the context.
// Dispatches on the type of S.