diff options
author | Marco Elver <elver@google.com> | 2025-05-26 17:03:55 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2025-05-26 17:03:55 +0200 |
commit | c7ccfc6dfc1c2d0ca9cf5615f9f95bb7ad78b1c9 (patch) | |
tree | 79aef42f5a494cc7350c07d46c170a92b685668b /clang/lib/Analysis/ThreadSafetyCommon.cpp | |
parent | 365dcf48b8aa726fb6a9ace4b37eb1f1cf121941 (diff) | |
download | llvm-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.cpp | 76 |
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. |