Skip to content

Commit ad4a513

Browse files
committed
[clang][CFG] Cleanup functions
Add declarations declared with attribute(cleanup(...)) to the CFG, similar to destructors. Differential Revision: https://reviews.llvm.org/D157385
1 parent 97495d3 commit ad4a513

File tree

5 files changed

+131
-14
lines changed

5 files changed

+131
-14
lines changed

clang/include/clang/Analysis/CFG.h

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,11 @@
1414
#ifndef LLVM_CLANG_ANALYSIS_CFG_H
1515
#define LLVM_CLANG_ANALYSIS_CFG_H
1616

17-
#include "clang/Analysis/Support/BumpVector.h"
18-
#include "clang/Analysis/ConstructionContext.h"
17+
#include "clang/AST/Attr.h"
1918
#include "clang/AST/ExprCXX.h"
2019
#include "clang/AST/ExprObjC.h"
20+
#include "clang/Analysis/ConstructionContext.h"
21+
#include "clang/Analysis/Support/BumpVector.h"
2122
#include "clang/Basic/LLVM.h"
2223
#include "llvm/ADT/DenseMap.h"
2324
#include "llvm/ADT/GraphTraits.h"
@@ -74,7 +75,8 @@ class CFGElement {
7475
MemberDtor,
7576
TemporaryDtor,
7677
DTOR_BEGIN = AutomaticObjectDtor,
77-
DTOR_END = TemporaryDtor
78+
DTOR_END = TemporaryDtor,
79+
CleanupFunction,
7880
};
7981

8082
protected:
@@ -383,6 +385,32 @@ class CFGImplicitDtor : public CFGElement {
383385
}
384386
};
385387

388+
class CFGCleanupFunction final : public CFGElement {
389+
public:
390+
CFGCleanupFunction() = default;
391+
CFGCleanupFunction(const VarDecl *VD)
392+
: CFGElement(Kind::CleanupFunction, VD) {
393+
assert(VD->hasAttr<CleanupAttr>());
394+
}
395+
396+
const VarDecl *getVarDecl() const {
397+
return static_cast<VarDecl *>(Data1.getPointer());
398+
}
399+
400+
/// Returns the function to be called when cleaning up the var decl.
401+
const FunctionDecl *getFunctionDecl() const {
402+
const CleanupAttr *A = getVarDecl()->getAttr<CleanupAttr>();
403+
return A->getFunctionDecl();
404+
}
405+
406+
private:
407+
friend class CFGElement;
408+
409+
static bool isKind(const CFGElement E) {
410+
return E.getKind() == Kind::CleanupFunction;
411+
}
412+
};
413+
386414
/// Represents C++ object destructor implicitly generated for automatic object
387415
/// or temporary bound to const reference at the point of leaving its local
388416
/// scope.
@@ -1142,6 +1170,10 @@ class CFGBlock {
11421170
Elements.push_back(CFGAutomaticObjDtor(VD, S), C);
11431171
}
11441172

1173+
void appendCleanupFunction(const VarDecl *VD, BumpVectorContext &C) {
1174+
Elements.push_back(CFGCleanupFunction(VD), C);
1175+
}
1176+
11451177
void appendLifetimeEnds(VarDecl *VD, Stmt *S, BumpVectorContext &C) {
11461178
Elements.push_back(CFGLifetimeEnds(VD, S), C);
11471179
}

clang/lib/Analysis/CFG.cpp

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -881,6 +881,10 @@ class CFGBuilder {
881881
B->appendAutomaticObjDtor(VD, S, cfg->getBumpVectorContext());
882882
}
883883

884+
void appendCleanupFunction(CFGBlock *B, VarDecl *VD) {
885+
B->appendCleanupFunction(VD, cfg->getBumpVectorContext());
886+
}
887+
884888
void appendLifetimeEnds(CFGBlock *B, VarDecl *VD, Stmt *S) {
885889
B->appendLifetimeEnds(VD, S, cfg->getBumpVectorContext());
886890
}
@@ -1346,7 +1350,8 @@ class CFGBuilder {
13461350
return {};
13471351
}
13481352

1349-
bool hasTrivialDestructor(VarDecl *VD);
1353+
bool hasTrivialDestructor(const VarDecl *VD) const;
1354+
bool needsAutomaticDestruction(const VarDecl *VD) const;
13501355
};
13511356

13521357
} // namespace
@@ -1861,14 +1866,14 @@ void CFGBuilder::addAutomaticObjDestruction(LocalScope::const_iterator B,
18611866
if (B == E)
18621867
return;
18631868

1864-
SmallVector<VarDecl *, 10> DeclsNonTrivial;
1865-
DeclsNonTrivial.reserve(B.distance(E));
1869+
SmallVector<VarDecl *, 10> DeclsNeedDestruction;
1870+
DeclsNeedDestruction.reserve(B.distance(E));
18661871

18671872
for (VarDecl* D : llvm::make_range(B, E))
1868-
if (!hasTrivialDestructor(D))
1869-
DeclsNonTrivial.push_back(D);
1873+
if (needsAutomaticDestruction(D))
1874+
DeclsNeedDestruction.push_back(D);
18701875

1871-
for (VarDecl *VD : llvm::reverse(DeclsNonTrivial)) {
1876+
for (VarDecl *VD : llvm::reverse(DeclsNeedDestruction)) {
18721877
if (BuildOpts.AddImplicitDtors) {
18731878
// If this destructor is marked as a no-return destructor, we need to
18741879
// create a new block for the destructor which does not have as a
@@ -1879,7 +1884,8 @@ void CFGBuilder::addAutomaticObjDestruction(LocalScope::const_iterator B,
18791884
Ty = getReferenceInitTemporaryType(VD->getInit());
18801885
Ty = Context->getBaseElementType(Ty);
18811886

1882-
if (Ty->getAsCXXRecordDecl()->isAnyDestructorNoReturn())
1887+
const CXXRecordDecl *CRD = Ty->getAsCXXRecordDecl();
1888+
if (CRD && CRD->isAnyDestructorNoReturn())
18831889
Block = createNoReturnBlock();
18841890
}
18851891

@@ -1890,8 +1896,10 @@ void CFGBuilder::addAutomaticObjDestruction(LocalScope::const_iterator B,
18901896
// objects, we end lifetime with scope end.
18911897
if (BuildOpts.AddLifetime)
18921898
appendLifetimeEnds(Block, VD, S);
1893-
if (BuildOpts.AddImplicitDtors)
1899+
if (BuildOpts.AddImplicitDtors && !hasTrivialDestructor(VD))
18941900
appendAutomaticObjDtor(Block, VD, S);
1901+
if (VD->hasAttr<CleanupAttr>())
1902+
appendCleanupFunction(Block, VD);
18951903
}
18961904
}
18971905

@@ -1922,7 +1930,7 @@ void CFGBuilder::addScopeExitHandling(LocalScope::const_iterator B,
19221930
// is destroyed, for automatic variables, this happens when the end of the
19231931
// scope is added.
19241932
for (VarDecl* D : llvm::make_range(B, E))
1925-
if (hasTrivialDestructor(D))
1933+
if (!needsAutomaticDestruction(D))
19261934
DeclsTrivial.push_back(D);
19271935

19281936
if (DeclsTrivial.empty())
@@ -2095,7 +2103,11 @@ LocalScope* CFGBuilder::addLocalScopeForDeclStmt(DeclStmt *DS,
20952103
return Scope;
20962104
}
20972105

2098-
bool CFGBuilder::hasTrivialDestructor(VarDecl *VD) {
2106+
bool CFGBuilder::needsAutomaticDestruction(const VarDecl *VD) const {
2107+
return !hasTrivialDestructor(VD) || VD->hasAttr<CleanupAttr>();
2108+
}
2109+
2110+
bool CFGBuilder::hasTrivialDestructor(const VarDecl *VD) const {
20992111
// Check for const references bound to temporary. Set type to pointee.
21002112
QualType QT = VD->getType();
21012113
if (QT->isReferenceType()) {
@@ -2149,7 +2161,7 @@ LocalScope* CFGBuilder::addLocalScopeForVarDecl(VarDecl *VD,
21492161
return Scope;
21502162

21512163
if (!BuildOpts.AddLifetime && !BuildOpts.AddScopes &&
2152-
hasTrivialDestructor(VD)) {
2164+
!needsAutomaticDestruction(VD)) {
21532165
assert(BuildOpts.AddImplicitDtors);
21542166
return Scope;
21552167
}
@@ -5287,6 +5299,7 @@ CFGImplicitDtor::getDestructorDecl(ASTContext &astContext) const {
52875299
case CFGElement::CXXRecordTypedCall:
52885300
case CFGElement::ScopeBegin:
52895301
case CFGElement::ScopeEnd:
5302+
case CFGElement::CleanupFunction:
52905303
llvm_unreachable("getDestructorDecl should only be used with "
52915304
"ImplicitDtors");
52925305
case CFGElement::AutomaticObjectDtor: {
@@ -5830,6 +5843,11 @@ static void print_elem(raw_ostream &OS, StmtPrinterHelper &Helper,
58305843
break;
58315844
}
58325845

5846+
case CFGElement::Kind::CleanupFunction:
5847+
OS << "CleanupFunction ("
5848+
<< E.castAs<CFGCleanupFunction>().getFunctionDecl()->getName() << ")\n";
5849+
break;
5850+
58335851
case CFGElement::Kind::LifetimeEnds:
58345852
Helper.handleDecl(E.castAs<CFGLifetimeEnds>().getVarDecl(), OS);
58355853
OS << " (Lifetime ends)\n";

clang/lib/Analysis/PathDiagnostic.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -567,6 +567,7 @@ getLocationForCaller(const StackFrameContext *SFC,
567567
}
568568
case CFGElement::ScopeBegin:
569569
case CFGElement::ScopeEnd:
570+
case CFGElement::CleanupFunction:
570571
llvm_unreachable("not yet implemented!");
571572
case CFGElement::LifetimeEnds:
572573
case CFGElement::LoopExit:

clang/lib/StaticAnalyzer/Core/ExprEngine.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -993,6 +993,7 @@ void ExprEngine::processCFGElement(const CFGElement E, ExplodedNode *Pred,
993993
ProcessLoopExit(E.castAs<CFGLoopExit>().getLoopStmt(), Pred);
994994
return;
995995
case CFGElement::LifetimeEnds:
996+
case CFGElement::CleanupFunction:
996997
case CFGElement::ScopeBegin:
997998
case CFGElement::ScopeEnd:
998999
return;

clang/test/Analysis/scopes-cfg-output.cpp

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1419,3 +1419,68 @@ void test_multiple_goto_entering_scopes() {
14191419
}
14201420
}
14211421
}
1422+
1423+
// CHECK: [B1]
1424+
// CHECK-NEXT: 1: CFGScopeBegin(i)
1425+
// CHECK-NEXT: 2: int i __attribute__((cleanup(cleanup_int)));
1426+
// CHECK-NEXT: 3: CleanupFunction (cleanup_int)
1427+
// CHECK-NEXT: 4: CFGScopeEnd(i)
1428+
void cleanup_int(int *i);
1429+
void test_cleanup_functions() {
1430+
int i __attribute__((cleanup(cleanup_int)));
1431+
}
1432+
1433+
// CHECK: [B1]
1434+
// CHECK-NEXT: 1: 10
1435+
// CHECK-NEXT: 2: i
1436+
// CHECK-NEXT: 3: [B1.2] = [B1.1]
1437+
// CHECK-NEXT: 4: return;
1438+
// CHECK-NEXT: 5: CleanupFunction (cleanup_int)
1439+
// CHECK-NEXT: 6: CFGScopeEnd(i)
1440+
// CHECK-NEXT: Preds (1): B3
1441+
// CHECK-NEXT: Succs (1): B0
1442+
// CHECK: [B2]
1443+
// CHECK-NEXT: 1: return;
1444+
// CHECK-NEXT: 2: CleanupFunction (cleanup_int)
1445+
// CHECK-NEXT: 3: CFGScopeEnd(i)
1446+
// CHECK-NEXT: Preds (1): B3
1447+
// CHECK-NEXT: Succs (1): B0
1448+
// CHECK: [B3]
1449+
// CHECK-NEXT: 1: CFGScopeBegin(i)
1450+
// CHECK-NEXT: 2: int i __attribute__((cleanup(cleanup_int)));
1451+
// CHECK-NEXT: 3: m
1452+
// CHECK-NEXT: 4: [B3.3] (ImplicitCastExpr, LValueToRValue, int)
1453+
// CHECK-NEXT: 5: 1
1454+
// CHECK-NEXT: 6: [B3.4] == [B3.5]
1455+
// CHECK-NEXT: T: if [B3.6]
1456+
// CHECK-NEXT: Preds (1): B4
1457+
// CHECK-NEXT: Succs (2): B2 B1
1458+
void test_cleanup_functions2(int m) {
1459+
int i __attribute__((cleanup(cleanup_int)));
1460+
1461+
if (m == 1) {
1462+
return;
1463+
}
1464+
1465+
i = 10;
1466+
return;
1467+
}
1468+
1469+
// CHECK: [B1]
1470+
// CHECK-NEXT: 1: CFGScopeBegin(f)
1471+
// CHECK-NEXT: 2: (CXXConstructExpr, [B1.3], F)
1472+
// CHECK-NEXT: 3: __attribute__((cleanup(cleanup_F))) F f;
1473+
// CHECK-NEXT: 4: CleanupFunction (cleanup_F)
1474+
// CHECK-NEXT: 5: [B1.3].~F() (Implicit destructor)
1475+
// CHECK-NEXT: 6: CFGScopeEnd(f)
1476+
// CHECK-NEXT: Preds (1): B2
1477+
// CHECK-NEXT: Succs (1): B0
1478+
class F {
1479+
public:
1480+
~F();
1481+
};
1482+
void cleanup_F(F *f);
1483+
1484+
void test() {
1485+
F f __attribute((cleanup(cleanup_F)));
1486+
}

0 commit comments

Comments
 (0)