tbaeder / rpms / clang

Forked from rpms/clang 3 years ago
Clone
efe2f45
commit cf8e189a99f988398a48148b9ea7901948665ab0
efe2f45
Author: Timm B├Ąder <tbaeder@redhat.com>
efe2f45
Date:   Wed Sep 6 12:19:20 2023 +0200
efe2f45
efe2f45
    [clang][TSA] Thread safety cleanup functions
efe2f45
    
efe2f45
    Consider cleanup functions in thread safety analysis.
efe2f45
    
efe2f45
    Differential Revision: https://reviews.llvm.org/D152504
efe2f45
efe2f45
diff --git a/clang/include/clang/Analysis/Analyses/ThreadSafetyCommon.h b/clang/include/clang/Analysis/Analyses/ThreadSafetyCommon.h
efe2f45
index 9d28325c1ea6..13e37ac2b56b 100644
efe2f45
--- a/clang/include/clang/Analysis/Analyses/ThreadSafetyCommon.h
efe2f45
+++ b/clang/include/clang/Analysis/Analyses/ThreadSafetyCommon.h
efe2f45
@@ -361,7 +361,7 @@ public:
efe2f45
     unsigned NumArgs = 0;
efe2f45
 
efe2f45
     // Function arguments
efe2f45
-    const Expr *const *FunArgs = nullptr;
efe2f45
+    llvm::PointerUnion<const Expr *const *, til::SExpr *> FunArgs = nullptr;
efe2f45
 
efe2f45
     // is Self referred to with -> or .?
efe2f45
     bool SelfArrow = false;
efe2f45
diff --git a/clang/lib/Analysis/ThreadSafety.cpp b/clang/lib/Analysis/ThreadSafety.cpp
efe2f45
index 3107d035254d..3e6ceb7d54c4 100644
efe2f45
--- a/clang/lib/Analysis/ThreadSafety.cpp
efe2f45
+++ b/clang/lib/Analysis/ThreadSafety.cpp
efe2f45
@@ -1773,7 +1773,8 @@ void BuildLockset::checkPtAccess(const Expr *Exp, AccessKind AK,
efe2f45
 ///
efe2f45
 /// \param Exp   The call expression.
efe2f45
 /// \param D     The callee declaration.
efe2f45
-/// \param Self  If \p Exp = nullptr, the implicit this argument.
efe2f45
+/// \param Self  If \p Exp = nullptr, the implicit this argument or the argument
efe2f45
+///              of an implicitly called cleanup function.
efe2f45
 /// \param Loc   If \p Exp = nullptr, the location.
efe2f45
 void BuildLockset::handleCall(const Expr *Exp, const NamedDecl *D,
efe2f45
                               til::LiteralPtr *Self, SourceLocation Loc) {
efe2f45
@@ -2417,6 +2418,15 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {
efe2f45
                                     AD.getTriggerStmt()->getEndLoc());
efe2f45
           break;
efe2f45
         }
efe2f45
+
efe2f45
+        case CFGElement::CleanupFunction: {
efe2f45
+          const CFGCleanupFunction &CF = BI.castAs<CFGCleanupFunction>();
efe2f45
+          LocksetBuilder.handleCall(/*Exp=*/nullptr, CF.getFunctionDecl(),
efe2f45
+                                    SxBuilder.createVariable(CF.getVarDecl()),
efe2f45
+                                    CF.getVarDecl()->getLocation());
efe2f45
+          break;
efe2f45
+        }
efe2f45
+
efe2f45
         case CFGElement::TemporaryDtor: {
efe2f45
           auto TD = BI.castAs<CFGTemporaryDtor>();
efe2f45
 
efe2f45
diff --git a/clang/lib/Analysis/ThreadSafetyCommon.cpp b/clang/lib/Analysis/ThreadSafetyCommon.cpp
efe2f45
index b8286cef396c..63cc66852a9e 100644
efe2f45
--- a/clang/lib/Analysis/ThreadSafetyCommon.cpp
efe2f45
+++ b/clang/lib/Analysis/ThreadSafetyCommon.cpp
efe2f45
@@ -110,7 +110,8 @@ static StringRef ClassifyDiagnostic(QualType VDT) {
efe2f45
 /// \param D       The declaration to which the attribute is attached.
efe2f45
 /// \param DeclExp An expression involving the Decl to which the attribute
efe2f45
 ///                is attached.  E.g. the call to a function.
efe2f45
-/// \param Self    S-expression to substitute for a \ref CXXThisExpr.
efe2f45
+/// \param Self    S-expression to substitute for a \ref CXXThisExpr in a call,
efe2f45
+///                or argument to a cleanup function.
efe2f45
 CapabilityExpr SExprBuilder::translateAttrExpr(const Expr *AttrExp,
efe2f45
                                                const NamedDecl *D,
efe2f45
                                                const Expr *DeclExp,
efe2f45
@@ -144,7 +145,11 @@ CapabilityExpr SExprBuilder::translateAttrExpr(const Expr *AttrExp,
efe2f45
 
efe2f45
   if (Self) {
efe2f45
     assert(!Ctx.SelfArg && "Ambiguous self argument");
efe2f45
-    Ctx.SelfArg = Self;
efe2f45
+    assert(isa<FunctionDecl>(D) && "Self argument requires function");
efe2f45
+    if (isa<CXXMethodDecl>(D))
efe2f45
+      Ctx.SelfArg = Self;
efe2f45
+    else
efe2f45
+      Ctx.FunArgs = Self;
efe2f45
 
efe2f45
     // If the attribute has no arguments, then assume the argument is "this".
efe2f45
     if (!AttrExp)
efe2f45
@@ -312,8 +317,14 @@ til::SExpr *SExprBuilder::translateDeclRefExpr(const DeclRefExpr *DRE,
efe2f45
               ? (cast<FunctionDecl>(D)->getCanonicalDecl() == Canonical)
efe2f45
               : (cast<ObjCMethodDecl>(D)->getCanonicalDecl() == Canonical)) {
efe2f45
         // Substitute call arguments for references to function parameters
efe2f45
-        assert(I < Ctx->NumArgs);
efe2f45
-        return translate(Ctx->FunArgs[I], Ctx->Prev);
efe2f45
+        if (const Expr *const *FunArgs =
efe2f45
+                Ctx->FunArgs.dyn_cast<const Expr *const *>()) {
efe2f45
+          assert(I < Ctx->NumArgs);
efe2f45
+          return translate(FunArgs[I], Ctx->Prev);
efe2f45
+        }
efe2f45
+
efe2f45
+        assert(I == 0);
efe2f45
+        return Ctx->FunArgs.get<til::SExpr *>();
efe2f45
       }
efe2f45
     }
efe2f45
     // Map the param back to the param of the original function declaration
efe2f45
diff --git a/clang/test/Sema/warn-thread-safety-analysis.c b/clang/test/Sema/warn-thread-safety-analysis.c
efe2f45
index 355616b73d96..642ea88ec3c9 100644
efe2f45
--- a/clang/test/Sema/warn-thread-safety-analysis.c
efe2f45
+++ b/clang/test/Sema/warn-thread-safety-analysis.c
efe2f45
@@ -72,6 +72,8 @@ int get_value(int *p) SHARED_LOCKS_REQUIRED(foo_.mu_){
efe2f45
   return *p;
efe2f45
 }
efe2f45
 
efe2f45
+void unlock_scope(struct Mutex *const *mu) __attribute__((release_capability(**mu)));
efe2f45
+
efe2f45
 int main(void) {
efe2f45
 
efe2f45
   Foo_fun1(1); // expected-warning{{calling function 'Foo_fun1' requires holding mutex 'mu2'}} \
efe2f45
@@ -127,6 +129,13 @@ int main(void) {
efe2f45
                                 // expected-note@-1{{mutex released here}}
efe2f45
   mutex_shared_unlock(&mu1;;    // expected-warning {{releasing mutex 'mu1' that was not held}}
efe2f45
 
efe2f45
+  /// Cleanup functions
efe2f45
+  {
efe2f45
+    struct Mutex* const __attribute__((cleanup(unlock_scope))) scope = &mu1;
efe2f45
+    mutex_exclusive_lock(scope);  // Note that we have to lock through scope, because no alias analysis!
efe2f45
+    // Cleanup happens automatically -> no warning.
efe2f45
+  }
efe2f45
+
efe2f45
   return 0;
efe2f45
 }
efe2f45