Index: clang/include/clang/Analysis/CFG.h =================================================================== --- clang/include/clang/Analysis/CFG.h +++ clang/include/clang/Analysis/CFG.h @@ -14,10 +14,11 @@ #ifndef LLVM_CLANG_ANALYSIS_CFG_H #define LLVM_CLANG_ANALYSIS_CFG_H -#include "clang/Analysis/Support/BumpVector.h" -#include "clang/Analysis/ConstructionContext.h" +#include "clang/AST/Attr.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/ExprObjC.h" +#include "clang/Analysis/ConstructionContext.h" +#include "clang/Analysis/Support/BumpVector.h" #include "clang/Basic/LLVM.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/GraphTraits.h" @@ -74,7 +75,8 @@ MemberDtor, TemporaryDtor, DTOR_BEGIN = AutomaticObjectDtor, - DTOR_END = TemporaryDtor + DTOR_END = TemporaryDtor, + CleanupFunction, }; protected: @@ -383,6 +385,32 @@ } }; +class CFGCleanupFunction final : public CFGElement { +public: + CFGCleanupFunction() = default; + CFGCleanupFunction(const VarDecl *VD) + : CFGElement(Kind::CleanupFunction, VD) { + assert(VD->hasAttr()); + } + + const VarDecl *getVarDecl() const { + return static_cast(Data1.getPointer()); + } + + /// Returns the function to be called when cleaning up the var decl. + const FunctionDecl *getFunctionDecl() const { + const CleanupAttr *A = getVarDecl()->getAttr(); + return A->getFunctionDecl(); + } + +private: + friend class CFGElement; + + static bool isKind(const CFGElement E) { + return E.getKind() == Kind::CleanupFunction; + } +}; + /// Represents C++ object destructor implicitly generated for automatic object /// or temporary bound to const reference at the point of leaving its local /// scope. @@ -1142,6 +1170,10 @@ Elements.push_back(CFGAutomaticObjDtor(VD, S), C); } + void appendCleanupFunction(const VarDecl *VD, BumpVectorContext &C) { + Elements.push_back(CFGCleanupFunction(VD), C); + } + void appendLifetimeEnds(VarDecl *VD, Stmt *S, BumpVectorContext &C) { Elements.push_back(CFGLifetimeEnds(VD, S), C); } Index: clang/lib/Analysis/CFG.cpp =================================================================== --- clang/lib/Analysis/CFG.cpp +++ clang/lib/Analysis/CFG.cpp @@ -881,6 +881,10 @@ B->appendAutomaticObjDtor(VD, S, cfg->getBumpVectorContext()); } + void appendCleanupFunction(CFGBlock *B, VarDecl *VD) { + B->appendCleanupFunction(VD, cfg->getBumpVectorContext()); + } + void appendLifetimeEnds(CFGBlock *B, VarDecl *VD, Stmt *S) { B->appendLifetimeEnds(VD, S, cfg->getBumpVectorContext()); } @@ -1346,7 +1350,8 @@ return {}; } - bool hasTrivialDestructor(VarDecl *VD); + bool hasTrivialDestructor(const VarDecl *VD) const; + bool needsAutomaticDestruction(const VarDecl *VD) const; }; } // namespace @@ -1861,14 +1866,14 @@ if (B == E) return; - SmallVector DeclsNonTrivial; - DeclsNonTrivial.reserve(B.distance(E)); + SmallVector DeclsNeedDestruction; + DeclsNeedDestruction.reserve(B.distance(E)); for (VarDecl* D : llvm::make_range(B, E)) - if (!hasTrivialDestructor(D)) - DeclsNonTrivial.push_back(D); + if (needsAutomaticDestruction(D)) + DeclsNeedDestruction.push_back(D); - for (VarDecl *VD : llvm::reverse(DeclsNonTrivial)) { + for (VarDecl *VD : llvm::reverse(DeclsNeedDestruction)) { if (BuildOpts.AddImplicitDtors) { // If this destructor is marked as a no-return destructor, we need to // create a new block for the destructor which does not have as a @@ -1879,7 +1884,8 @@ Ty = getReferenceInitTemporaryType(VD->getInit()); Ty = Context->getBaseElementType(Ty); - if (Ty->getAsCXXRecordDecl()->isAnyDestructorNoReturn()) + const CXXRecordDecl *CRD = Ty->getAsCXXRecordDecl(); + if (CRD && CRD->isAnyDestructorNoReturn()) Block = createNoReturnBlock(); } @@ -1890,8 +1896,10 @@ // objects, we end lifetime with scope end. if (BuildOpts.AddLifetime) appendLifetimeEnds(Block, VD, S); - if (BuildOpts.AddImplicitDtors) + if (BuildOpts.AddImplicitDtors && !hasTrivialDestructor(VD)) appendAutomaticObjDtor(Block, VD, S); + if (VD->hasAttr()) + appendCleanupFunction(Block, VD); } } @@ -1922,7 +1930,7 @@ // is destroyed, for automatic variables, this happens when the end of the // scope is added. for (VarDecl* D : llvm::make_range(B, E)) - if (hasTrivialDestructor(D)) + if (!needsAutomaticDestruction(D)) DeclsTrivial.push_back(D); if (DeclsTrivial.empty()) @@ -2095,7 +2103,11 @@ return Scope; } -bool CFGBuilder::hasTrivialDestructor(VarDecl *VD) { +bool CFGBuilder::needsAutomaticDestruction(const VarDecl *VD) const { + return !hasTrivialDestructor(VD) || VD->hasAttr(); +} + +bool CFGBuilder::hasTrivialDestructor(const VarDecl *VD) const { // Check for const references bound to temporary. Set type to pointee. QualType QT = VD->getType(); if (QT->isReferenceType()) { @@ -2149,7 +2161,7 @@ return Scope; if (!BuildOpts.AddLifetime && !BuildOpts.AddScopes && - hasTrivialDestructor(VD)) { + !needsAutomaticDestruction(VD)) { assert(BuildOpts.AddImplicitDtors); return Scope; } @@ -5287,6 +5299,7 @@ case CFGElement::CXXRecordTypedCall: case CFGElement::ScopeBegin: case CFGElement::ScopeEnd: + case CFGElement::CleanupFunction: llvm_unreachable("getDestructorDecl should only be used with " "ImplicitDtors"); case CFGElement::AutomaticObjectDtor: { @@ -5830,6 +5843,11 @@ break; } + case CFGElement::Kind::CleanupFunction: + OS << "CleanupFunction (" + << E.castAs().getFunctionDecl()->getName() << ")\n"; + break; + case CFGElement::Kind::LifetimeEnds: Helper.handleDecl(E.castAs().getVarDecl(), OS); OS << " (Lifetime ends)\n"; Index: clang/lib/Analysis/PathDiagnostic.cpp =================================================================== --- clang/lib/Analysis/PathDiagnostic.cpp +++ clang/lib/Analysis/PathDiagnostic.cpp @@ -567,6 +567,7 @@ } case CFGElement::ScopeBegin: case CFGElement::ScopeEnd: + case CFGElement::CleanupFunction: llvm_unreachable("not yet implemented!"); case CFGElement::LifetimeEnds: case CFGElement::LoopExit: Index: clang/lib/StaticAnalyzer/Core/ExprEngine.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -993,6 +993,7 @@ ProcessLoopExit(E.castAs().getLoopStmt(), Pred); return; case CFGElement::LifetimeEnds: + case CFGElement::CleanupFunction: case CFGElement::ScopeBegin: case CFGElement::ScopeEnd: return; Index: clang/test/Analysis/scopes-cfg-output.cpp =================================================================== --- clang/test/Analysis/scopes-cfg-output.cpp +++ clang/test/Analysis/scopes-cfg-output.cpp @@ -1419,3 +1419,68 @@ } } } + +// CHECK: [B1] +// CHECK-NEXT: 1: CFGScopeBegin(i) +// CHECK-NEXT: 2: int i __attribute__((cleanup(cleanup_int))); +// CHECK-NEXT: 3: CleanupFunction (cleanup_int) +// CHECK-NEXT: 4: CFGScopeEnd(i) +void cleanup_int(int *i); +void test_cleanup_functions() { + int i __attribute__((cleanup(cleanup_int))); +} + +// CHECK: [B1] +// CHECK-NEXT: 1: 10 +// CHECK-NEXT: 2: i +// CHECK-NEXT: 3: [B1.2] = [B1.1] +// CHECK-NEXT: 4: return; +// CHECK-NEXT: 5: CleanupFunction (cleanup_int) +// CHECK-NEXT: 6: CFGScopeEnd(i) +// CHECK-NEXT: Preds (1): B3 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B2] +// CHECK-NEXT: 1: return; +// CHECK-NEXT: 2: CleanupFunction (cleanup_int) +// CHECK-NEXT: 3: CFGScopeEnd(i) +// CHECK-NEXT: Preds (1): B3 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B3] +// CHECK-NEXT: 1: CFGScopeBegin(i) +// CHECK-NEXT: 2: int i __attribute__((cleanup(cleanup_int))); +// CHECK-NEXT: 3: m +// CHECK-NEXT: 4: [B3.3] (ImplicitCastExpr, LValueToRValue, int) +// CHECK-NEXT: 5: 1 +// CHECK-NEXT: 6: [B3.4] == [B3.5] +// CHECK-NEXT: T: if [B3.6] +// CHECK-NEXT: Preds (1): B4 +// CHECK-NEXT: Succs (2): B2 B1 +void test_cleanup_functions2(int m) { + int i __attribute__((cleanup(cleanup_int))); + + if (m == 1) { + return; + } + + i = 10; + return; +} + +// CHECK: [B1] +// CHECK-NEXT: 1: CFGScopeBegin(f) +// CHECK-NEXT: 2: (CXXConstructExpr, [B1.3], F) +// CHECK-NEXT: 3: __attribute__((cleanup(cleanup_F))) F f; +// CHECK-NEXT: 4: CleanupFunction (cleanup_F) +// CHECK-NEXT: 5: [B1.3].~F() (Implicit destructor) +// CHECK-NEXT: 6: CFGScopeEnd(f) +// CHECK-NEXT: Preds (1): B2 +// CHECK-NEXT: Succs (1): B0 +class F { +public: + ~F(); +}; +void cleanup_F(F *f); + +void test() { + F f __attribute((cleanup(cleanup_F))); +}