tstellar / rpms / clang

Forked from rpms/clang 6 years ago
Clone
efe2f45
commit ad4a5130277776d8f15f40ac5a6dede6ad3aabfb
efe2f45
Author: Timm B├Ąder <tbaeder@redhat.com>
efe2f45
Date:   Tue Aug 8 14:11:33 2023 +0200
efe2f45
efe2f45
    [clang][CFG] Cleanup functions
efe2f45
    
efe2f45
    Add declarations declared with attribute(cleanup(...)) to the CFG,
efe2f45
    similar to destructors.
efe2f45
    
efe2f45
    Differential Revision: https://reviews.llvm.org/D157385
efe2f45
efe2f45
diff --git a/clang/include/clang/Analysis/CFG.h b/clang/include/clang/Analysis/CFG.h
efe2f45
index cf4fa2da2a35..67383bb316d3 100644
efe2f45
--- a/clang/include/clang/Analysis/CFG.h
efe2f45
+++ b/clang/include/clang/Analysis/CFG.h
efe2f45
@@ -14,10 +14,11 @@
efe2f45
 #ifndef LLVM_CLANG_ANALYSIS_CFG_H
efe2f45
 #define LLVM_CLANG_ANALYSIS_CFG_H
efe2f45
 
efe2f45
-#include "clang/Analysis/Support/BumpVector.h"
efe2f45
-#include "clang/Analysis/ConstructionContext.h"
efe2f45
+#include "clang/AST/Attr.h"
efe2f45
 #include "clang/AST/ExprCXX.h"
efe2f45
 #include "clang/AST/ExprObjC.h"
efe2f45
+#include "clang/Analysis/ConstructionContext.h"
efe2f45
+#include "clang/Analysis/Support/BumpVector.h"
efe2f45
 #include "clang/Basic/LLVM.h"
efe2f45
 #include "llvm/ADT/DenseMap.h"
efe2f45
 #include "llvm/ADT/GraphTraits.h"
efe2f45
@@ -74,7 +75,8 @@ public:
efe2f45
     MemberDtor,
efe2f45
     TemporaryDtor,
efe2f45
     DTOR_BEGIN = AutomaticObjectDtor,
efe2f45
-    DTOR_END = TemporaryDtor
efe2f45
+    DTOR_END = TemporaryDtor,
efe2f45
+    CleanupFunction,
efe2f45
   };
efe2f45
 
efe2f45
 protected:
efe2f45
@@ -383,6 +385,32 @@ private:
efe2f45
   }
efe2f45
 };
efe2f45
 
efe2f45
+class CFGCleanupFunction final : public CFGElement {
efe2f45
+public:
efe2f45
+  CFGCleanupFunction() = default;
efe2f45
+  CFGCleanupFunction(const VarDecl *VD)
efe2f45
+      : CFGElement(Kind::CleanupFunction, VD) {
efe2f45
+    assert(VD->hasAttr<CleanupAttr>());
efe2f45
+  }
efe2f45
+
efe2f45
+  const VarDecl *getVarDecl() const {
efe2f45
+    return static_cast<VarDecl *>(Data1.getPointer());
efe2f45
+  }
efe2f45
+
efe2f45
+  /// Returns the function to be called when cleaning up the var decl.
efe2f45
+  const FunctionDecl *getFunctionDecl() const {
efe2f45
+    const CleanupAttr *A = getVarDecl()->getAttr<CleanupAttr>();
efe2f45
+    return A->getFunctionDecl();
efe2f45
+  }
efe2f45
+
efe2f45
+private:
efe2f45
+  friend class CFGElement;
efe2f45
+
efe2f45
+  static bool isKind(const CFGElement E) {
efe2f45
+    return E.getKind() == Kind::CleanupFunction;
efe2f45
+  }
efe2f45
+};
efe2f45
+
efe2f45
 /// Represents C++ object destructor implicitly generated for automatic object
efe2f45
 /// or temporary bound to const reference at the point of leaving its local
efe2f45
 /// scope.
efe2f45
@@ -1142,6 +1170,10 @@ public:
efe2f45
     Elements.push_back(CFGAutomaticObjDtor(VD, S), C);
efe2f45
   }
efe2f45
 
efe2f45
+  void appendCleanupFunction(const VarDecl *VD, BumpVectorContext &C) {
efe2f45
+    Elements.push_back(CFGCleanupFunction(VD), C);
efe2f45
+  }
efe2f45
+
efe2f45
   void appendLifetimeEnds(VarDecl *VD, Stmt *S, BumpVectorContext &C) {
efe2f45
     Elements.push_back(CFGLifetimeEnds(VD, S), C);
efe2f45
   }
efe2f45
diff --git a/clang/lib/Analysis/CFG.cpp b/clang/lib/Analysis/CFG.cpp
efe2f45
index b82f9010a83f..03ab4c6fdf29 100644
efe2f45
--- a/clang/lib/Analysis/CFG.cpp
efe2f45
+++ b/clang/lib/Analysis/CFG.cpp
efe2f45
@@ -881,6 +881,10 @@ private:
efe2f45
     B->appendAutomaticObjDtor(VD, S, cfg->getBumpVectorContext());
efe2f45
   }
efe2f45
 
efe2f45
+  void appendCleanupFunction(CFGBlock *B, VarDecl *VD) {
efe2f45
+    B->appendCleanupFunction(VD, cfg->getBumpVectorContext());
efe2f45
+  }
efe2f45
+
efe2f45
   void appendLifetimeEnds(CFGBlock *B, VarDecl *VD, Stmt *S) {
efe2f45
     B->appendLifetimeEnds(VD, S, cfg->getBumpVectorContext());
efe2f45
   }
efe2f45
@@ -1346,7 +1350,8 @@ private:
efe2f45
     return {};
efe2f45
   }
efe2f45
 
efe2f45
-  bool hasTrivialDestructor(VarDecl *VD);
efe2f45
+  bool hasTrivialDestructor(const VarDecl *VD) const;
efe2f45
+  bool needsAutomaticDestruction(const VarDecl *VD) const;
efe2f45
 };
efe2f45
 
efe2f45
 } // namespace
efe2f45
@@ -1861,14 +1866,14 @@ void CFGBuilder::addAutomaticObjDestruction(LocalScope::const_iterator B,
efe2f45
   if (B == E)
efe2f45
     return;
efe2f45
 
efe2f45
-  SmallVector<VarDecl *, 10> DeclsNonTrivial;
efe2f45
-  DeclsNonTrivial.reserve(B.distance(E));
efe2f45
+  SmallVector<VarDecl *, 10> DeclsNeedDestruction;
efe2f45
+  DeclsNeedDestruction.reserve(B.distance(E));
efe2f45
 
efe2f45
   for (VarDecl* D : llvm::make_range(B, E))
efe2f45
-    if (!hasTrivialDestructor(D))
efe2f45
-      DeclsNonTrivial.push_back(D);
efe2f45
+    if (needsAutomaticDestruction(D))
efe2f45
+      DeclsNeedDestruction.push_back(D);
efe2f45
 
efe2f45
-  for (VarDecl *VD : llvm::reverse(DeclsNonTrivial)) {
efe2f45
+  for (VarDecl *VD : llvm::reverse(DeclsNeedDestruction)) {
efe2f45
     if (BuildOpts.AddImplicitDtors) {
efe2f45
       // If this destructor is marked as a no-return destructor, we need to
efe2f45
       // create a new block for the destructor which does not have as a
efe2f45
@@ -1879,7 +1884,8 @@ void CFGBuilder::addAutomaticObjDestruction(LocalScope::const_iterator B,
efe2f45
         Ty = getReferenceInitTemporaryType(VD->getInit());
efe2f45
       Ty = Context->getBaseElementType(Ty);
efe2f45
 
efe2f45
-      if (Ty->getAsCXXRecordDecl()->isAnyDestructorNoReturn())
efe2f45
+      const CXXRecordDecl *CRD = Ty->getAsCXXRecordDecl();
efe2f45
+      if (CRD && CRD->isAnyDestructorNoReturn())
efe2f45
         Block = createNoReturnBlock();
efe2f45
     }
efe2f45
 
efe2f45
@@ -1890,8 +1896,10 @@ void CFGBuilder::addAutomaticObjDestruction(LocalScope::const_iterator B,
efe2f45
     // objects, we end lifetime with scope end.
efe2f45
     if (BuildOpts.AddLifetime)
efe2f45
       appendLifetimeEnds(Block, VD, S);
efe2f45
-    if (BuildOpts.AddImplicitDtors)
efe2f45
+    if (BuildOpts.AddImplicitDtors && !hasTrivialDestructor(VD))
efe2f45
       appendAutomaticObjDtor(Block, VD, S);
efe2f45
+    if (VD->hasAttr<CleanupAttr>())
efe2f45
+      appendCleanupFunction(Block, VD);
efe2f45
   }
efe2f45
 }
efe2f45
 
efe2f45
@@ -1922,7 +1930,7 @@ void CFGBuilder::addScopeExitHandling(LocalScope::const_iterator B,
efe2f45
   // is destroyed, for automatic variables, this happens when the end of the
efe2f45
   // scope is added.
efe2f45
   for (VarDecl* D : llvm::make_range(B, E))
efe2f45
-    if (hasTrivialDestructor(D))
efe2f45
+    if (!needsAutomaticDestruction(D))
efe2f45
       DeclsTrivial.push_back(D);
efe2f45
 
efe2f45
   if (DeclsTrivial.empty())
efe2f45
@@ -2095,7 +2103,11 @@ LocalScope* CFGBuilder::addLocalScopeForDeclStmt(DeclStmt *DS,
efe2f45
   return Scope;
efe2f45
 }
efe2f45
 
efe2f45
-bool CFGBuilder::hasTrivialDestructor(VarDecl *VD) {
efe2f45
+bool CFGBuilder::needsAutomaticDestruction(const VarDecl *VD) const {
efe2f45
+  return !hasTrivialDestructor(VD) || VD->hasAttr<CleanupAttr>();
efe2f45
+}
efe2f45
+
efe2f45
+bool CFGBuilder::hasTrivialDestructor(const VarDecl *VD) const {
efe2f45
   // Check for const references bound to temporary. Set type to pointee.
efe2f45
   QualType QT = VD->getType();
efe2f45
   if (QT->isReferenceType()) {
efe2f45
@@ -2149,7 +2161,7 @@ LocalScope* CFGBuilder::addLocalScopeForVarDecl(VarDecl *VD,
efe2f45
     return Scope;
efe2f45
 
efe2f45
   if (!BuildOpts.AddLifetime && !BuildOpts.AddScopes &&
efe2f45
-      hasTrivialDestructor(VD)) {
efe2f45
+      !needsAutomaticDestruction(VD)) {
efe2f45
     assert(BuildOpts.AddImplicitDtors);
efe2f45
     return Scope;
efe2f45
   }
efe2f45
@@ -5287,6 +5299,7 @@ CFGImplicitDtor::getDestructorDecl(ASTContext &astContext) const {
efe2f45
     case CFGElement::CXXRecordTypedCall:
efe2f45
     case CFGElement::ScopeBegin:
efe2f45
     case CFGElement::ScopeEnd:
efe2f45
+    case CFGElement::CleanupFunction:
efe2f45
       llvm_unreachable("getDestructorDecl should only be used with "
efe2f45
                        "ImplicitDtors");
efe2f45
     case CFGElement::AutomaticObjectDtor: {
efe2f45
@@ -5830,6 +5843,11 @@ static void print_elem(raw_ostream &OS, StmtPrinterHelper &Helper,
efe2f45
     break;
efe2f45
   }
efe2f45
 
efe2f45
+  case CFGElement::Kind::CleanupFunction:
efe2f45
+    OS << "CleanupFunction ("
efe2f45
+       << E.castAs<CFGCleanupFunction>().getFunctionDecl()->getName() << ")\n";
efe2f45
+    break;
efe2f45
+
efe2f45
   case CFGElement::Kind::LifetimeEnds:
efe2f45
     Helper.handleDecl(E.castAs<CFGLifetimeEnds>().getVarDecl(), OS);
efe2f45
     OS << " (Lifetime ends)\n";
efe2f45
diff --git a/clang/lib/Analysis/PathDiagnostic.cpp b/clang/lib/Analysis/PathDiagnostic.cpp
efe2f45
index 348afc42319e..0cb03943c547 100644
efe2f45
--- a/clang/lib/Analysis/PathDiagnostic.cpp
efe2f45
+++ b/clang/lib/Analysis/PathDiagnostic.cpp
efe2f45
@@ -567,6 +567,7 @@ getLocationForCaller(const StackFrameContext *SFC,
efe2f45
   }
efe2f45
   case CFGElement::ScopeBegin:
efe2f45
   case CFGElement::ScopeEnd:
efe2f45
+  case CFGElement::CleanupFunction:
efe2f45
     llvm_unreachable("not yet implemented!");
efe2f45
   case CFGElement::LifetimeEnds:
efe2f45
   case CFGElement::LoopExit:
efe2f45
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
efe2f45
index 0e2ac78f7089..d7c5bd1d4042 100644
efe2f45
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
efe2f45
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
efe2f45
@@ -993,6 +993,7 @@ void ExprEngine::processCFGElement(const CFGElement E, ExplodedNode *Pred,
efe2f45
       ProcessLoopExit(E.castAs<CFGLoopExit>().getLoopStmt(), Pred);
efe2f45
       return;
efe2f45
     case CFGElement::LifetimeEnds:
efe2f45
+    case CFGElement::CleanupFunction:
efe2f45
     case CFGElement::ScopeBegin:
efe2f45
     case CFGElement::ScopeEnd:
efe2f45
       return;
efe2f45
diff --git a/clang/test/Analysis/scopes-cfg-output.cpp b/clang/test/Analysis/scopes-cfg-output.cpp
efe2f45
index 6877d124e67a..4eb8967e3735 100644
efe2f45
--- a/clang/test/Analysis/scopes-cfg-output.cpp
efe2f45
+++ b/clang/test/Analysis/scopes-cfg-output.cpp
efe2f45
@@ -1419,3 +1419,68 @@ label:
efe2f45
     }
efe2f45
   }
efe2f45
 }
efe2f45
+
efe2f45
+// CHECK:      [B1]
efe2f45
+// CHECK-NEXT:   1: CFGScopeBegin(i)
efe2f45
+// CHECK-NEXT:   2: int i __attribute__((cleanup(cleanup_int)));
efe2f45
+// CHECK-NEXT:   3: CleanupFunction (cleanup_int)
efe2f45
+// CHECK-NEXT:   4: CFGScopeEnd(i)
efe2f45
+void cleanup_int(int *i);
efe2f45
+void test_cleanup_functions() {
efe2f45
+  int i __attribute__((cleanup(cleanup_int)));
efe2f45
+}
efe2f45
+
efe2f45
+// CHECK:      [B1]
efe2f45
+// CHECK-NEXT:    1: 10
efe2f45
+// CHECK-NEXT:    2: i
efe2f45
+// CHECK-NEXT:    3: [B1.2] = [B1.1]
efe2f45
+// CHECK-NEXT:    4: return;
efe2f45
+// CHECK-NEXT:    5: CleanupFunction (cleanup_int)
efe2f45
+// CHECK-NEXT:    6: CFGScopeEnd(i)
efe2f45
+// CHECK-NEXT:    Preds (1): B3
efe2f45
+// CHECK-NEXT:    Succs (1): B0
efe2f45
+// CHECK:      [B2]
efe2f45
+// CHECK-NEXT:    1: return;
efe2f45
+// CHECK-NEXT:    2: CleanupFunction (cleanup_int)
efe2f45
+// CHECK-NEXT:    3: CFGScopeEnd(i)
efe2f45
+// CHECK-NEXT:    Preds (1): B3
efe2f45
+// CHECK-NEXT:    Succs (1): B0
efe2f45
+// CHECK:      [B3]
efe2f45
+// CHECK-NEXT:    1: CFGScopeBegin(i)
efe2f45
+// CHECK-NEXT:    2: int i __attribute__((cleanup(cleanup_int)));
efe2f45
+// CHECK-NEXT:    3: m
efe2f45
+// CHECK-NEXT:    4: [B3.3] (ImplicitCastExpr, LValueToRValue, int)
efe2f45
+// CHECK-NEXT:    5: 1
efe2f45
+// CHECK-NEXT:    6: [B3.4] == [B3.5]
efe2f45
+// CHECK-NEXT:    T: if [B3.6]
efe2f45
+// CHECK-NEXT:    Preds (1): B4
efe2f45
+// CHECK-NEXT:    Succs (2): B2 B1
efe2f45
+void test_cleanup_functions2(int m) {
efe2f45
+  int i __attribute__((cleanup(cleanup_int)));
efe2f45
+
efe2f45
+  if (m == 1) {
efe2f45
+    return;
efe2f45
+  }
efe2f45
+
efe2f45
+  i = 10;
efe2f45
+  return;
efe2f45
+}
efe2f45
+
efe2f45
+// CHECK:       [B1]
efe2f45
+// CHECK-NEXT:    1: CFGScopeBegin(f)
efe2f45
+// CHECK-NEXT:    2:  (CXXConstructExpr, [B1.3], F)
efe2f45
+// CHECK-NEXT:    3: F f __attribute__((cleanup(cleanup_F)));
efe2f45
+// CHECK-NEXT:    4: CleanupFunction (cleanup_F)
efe2f45
+// CHECK-NEXT:    5: [B1.3].~F() (Implicit destructor)
efe2f45
+// CHECK-NEXT:    6: CFGScopeEnd(f)
efe2f45
+// CHECK-NEXT:    Preds (1): B2
efe2f45
+// CHECK-NEXT:    Succs (1): B0
efe2f45
+class F {
efe2f45
+public:
efe2f45
+  ~F();
efe2f45
+};
efe2f45
+void cleanup_F(F *f);
efe2f45
+
efe2f45
+void test() {
efe2f45
+  F f __attribute((cleanup(cleanup_F)));
efe2f45
+}