From: Jordan Rose Date: Tue, 3 Sep 2013 17:00:57 +0000 (+0000) Subject: Add an implicit dtor CFG node just before C++ 'delete' expressions. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=36d558d85653315edb389677e995ec9ccdbfbf3d;p=clang Add an implicit dtor CFG node just before C++ 'delete' expressions. This paves the way for adding support for modeling the destructor of a region before it is deleted. The statement "delete " now generates this series of CFG elements: 1. 2. [B1.1]->~Foo() (Implicit destructor) 3. delete [B1.1] Patch by Karthik Bhat! git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@189828 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Analysis/CFG.h b/include/clang/Analysis/CFG.h index d42b19e1d5..2a60135600 100644 --- a/include/clang/Analysis/CFG.h +++ b/include/clang/Analysis/CFG.h @@ -43,6 +43,8 @@ namespace clang { class PrinterHelper; class LangOptions; class ASTContext; + class CXXRecordDecl; + class CXXDeleteExpr; /// CFGElement - Represents a top-level expression in a basic block. class CFGElement { @@ -53,6 +55,7 @@ public: Initializer, // dtor kind AutomaticObjectDtor, + DeleteDtor, BaseDtor, MemberDtor, TemporaryDtor, @@ -185,6 +188,31 @@ private: } }; +/// CFGDeleteDtor - Represents C++ object destructor generated +/// from a call to delete. +class CFGDeleteDtor : public CFGImplicitDtor { +public: + CFGDeleteDtor(const CXXRecordDecl *RD, const CXXDeleteExpr *DE) + : CFGImplicitDtor(DeleteDtor, RD, DE) {} + + const CXXRecordDecl *getCXXRecordDecl() const { + return static_cast(Data1.getPointer()); + } + + // Get Delete expression which triggered the destructor call. + const CXXDeleteExpr *getDeleteExpr() { + return static_cast(Data2.getPointer()); + } + + +private: + friend class CFGElement; + CFGDeleteDtor() {} + static bool isKind(const CFGElement &elem) { + return elem.getKind() == DeleteDtor; + } +}; + /// CFGBaseDtor - Represents C++ object destructor implicitly generated for /// base object in destructor. class CFGBaseDtor : public CFGImplicitDtor { @@ -564,6 +592,10 @@ public: Elements.push_back(CFGAutomaticObjDtor(VD, S), C); } + void appendDeleteDtor(CXXRecordDecl *RD, CXXDeleteExpr *DE, BumpVectorContext &C) { + Elements.push_back(CFGDeleteDtor(RD, DE), C); + } + // Destructors must be inserted in reversed order. So insertion is in two // steps. First we prepare space for some number of elements, then we insert // the elements beginning at the last position in prepared space. diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h index 33e4431eb4..da9ee50a34 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h @@ -203,6 +203,8 @@ public: void ProcessAutomaticObjDtor(const CFGAutomaticObjDtor D, ExplodedNode *Pred, ExplodedNodeSet &Dst); + void ProcessDeleteDtor(const CFGDeleteDtor D, + ExplodedNode *Pred, ExplodedNodeSet &Dst); void ProcessBaseDtor(const CFGBaseDtor D, ExplodedNode *Pred, ExplodedNodeSet &Dst); void ProcessMemberDtor(const CFGMemberDtor D, diff --git a/lib/Analysis/CFG.cpp b/lib/Analysis/CFG.cpp index ad0acb7abe..5e03f4377d 100644 --- a/lib/Analysis/CFG.cpp +++ b/lib/Analysis/CFG.cpp @@ -363,6 +363,7 @@ private: AddStmtChoice asc); CFGBlock *VisitCXXCatchStmt(CXXCatchStmt *S); CFGBlock *VisitCXXConstructExpr(CXXConstructExpr *C, AddStmtChoice asc); + CFGBlock *VisitCXXDeleteExpr(CXXDeleteExpr *DE, AddStmtChoice asc); CFGBlock *VisitCXXForRangeStmt(CXXForRangeStmt *S); CFGBlock *VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr *E, AddStmtChoice asc); @@ -471,6 +472,10 @@ private: B->appendAutomaticObjDtor(VD, S, cfg->getBumpVectorContext()); } + void appendDeleteDtor(CFGBlock *B, CXXRecordDecl *RD, CXXDeleteExpr *DE) { + B->appendDeleteDtor(RD, DE, cfg->getBumpVectorContext()); + } + void prependAutomaticObjDtorsWithTerminator(CFGBlock *Blk, LocalScope::const_iterator B, LocalScope::const_iterator E); @@ -1117,6 +1122,9 @@ CFGBlock *CFGBuilder::Visit(Stmt * S, AddStmtChoice asc) { case Stmt::CXXConstructExprClass: return VisitCXXConstructExpr(cast(S), asc); + case Stmt::CXXDeleteExprClass: + return VisitCXXDeleteExpr(cast(S), asc); + case Stmt::CXXFunctionalCastExprClass: return VisitCXXFunctionalCastExpr(cast(S), asc); @@ -3113,6 +3121,22 @@ CFGBlock *CFGBuilder::VisitCXXConstructExpr(CXXConstructExpr *C, return VisitChildren(C); } + +CFGBlock *CFGBuilder::VisitCXXDeleteExpr(CXXDeleteExpr *DE, + AddStmtChoice asc) { + autoCreateBlock(); + appendStmt(Block, DE); + QualType DTy = DE->getDestroyedType(); + DTy = DTy.getNonReferenceType(); + CXXRecordDecl *RD = Context->getBaseElementType(DTy)->getAsCXXRecordDecl(); + if (RD) { + if (!RD->hasTrivialDestructor()) + appendDeleteDtor(Block, RD, DE); + } + + return VisitChildren(DE); +} + CFGBlock *CFGBuilder::VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr *E, AddStmtChoice asc) { if (asc.alwaysAdd(*this, E)) { @@ -3413,6 +3437,14 @@ CFGImplicitDtor::getDestructorDecl(ASTContext &astContext) const { cast(recordType->getDecl()); return classDecl->getDestructor(); } + case CFGElement::DeleteDtor: { + const CXXDeleteExpr *DE = castAs().getDeleteExpr(); + QualType DTy = DE->getDestroyedType(); + DTy = DTy.getNonReferenceType(); + const CXXRecordDecl *classDecl = + astContext.getBaseElementType(DTy)->getAsCXXRecordDecl(); + return classDecl->getDestructor(); + } case CFGElement::TemporaryDtor: { const CXXBindTemporaryExpr *bindExpr = castAs().getBindTemporaryExpr(); @@ -3751,6 +3783,15 @@ static void print_elem(raw_ostream &OS, StmtPrinterHelper* Helper, OS << ".~" << T->getAsCXXRecordDecl()->getName().str() << "()"; OS << " (Implicit destructor)\n"; + } else if (Optional DE = E.getAs()) { + const CXXRecordDecl *RD = DE->getCXXRecordDecl(); + if (!RD) + return; + CXXDeleteExpr *DelExpr = + const_cast(DE->getDeleteExpr()); + Helper->handledStmt(cast(DelExpr->getArgument()), OS); + OS << "->~" << RD->getName().str() << "()"; + OS << " (Implicit destructor)\n"; } else if (Optional BE = E.getAs()) { const CXXBaseSpecifier *BS = BE->getBaseSpecifier(); OS << "~" << BS->getType()->getAsCXXRecordDecl()->getName() << "()"; diff --git a/lib/StaticAnalyzer/Core/ExprEngine.cpp b/lib/StaticAnalyzer/Core/ExprEngine.cpp index 79139df786..16369f0a2b 100644 --- a/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -287,6 +287,7 @@ void ExprEngine::processCFGElement(const CFGElement E, ExplodedNode *Pred, ProcessInitializer(E.castAs().getInitializer(), Pred); return; case CFGElement::AutomaticObjectDtor: + case CFGElement::DeleteDtor: case CFGElement::BaseDtor: case CFGElement::MemberDtor: case CFGElement::TemporaryDtor: @@ -535,6 +536,9 @@ void ExprEngine::ProcessImplicitDtor(const CFGImplicitDtor D, case CFGElement::TemporaryDtor: ProcessTemporaryDtor(D.castAs(), Pred, Dst); break; + case CFGElement::DeleteDtor: + ProcessDeleteDtor(D.castAs(), Pred, Dst); + break; default: llvm_unreachable("Unexpected dtor kind."); } @@ -562,6 +566,12 @@ void ExprEngine::ProcessAutomaticObjDtor(const CFGAutomaticObjDtor Dtor, Pred, Dst); } +void ExprEngine::ProcessDeleteDtor(const CFGDeleteDtor Dtor, + ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + //TODO: Handle DeleteDtor +} + void ExprEngine::ProcessBaseDtor(const CFGBaseDtor D, ExplodedNode *Pred, ExplodedNodeSet &Dst) { const LocationContext *LCtx = Pred->getLocationContext(); diff --git a/lib/StaticAnalyzer/Core/PathDiagnostic.cpp b/lib/StaticAnalyzer/Core/PathDiagnostic.cpp index 33c7a82d8e..8099cd3b74 100644 --- a/lib/StaticAnalyzer/Core/PathDiagnostic.cpp +++ b/lib/StaticAnalyzer/Core/PathDiagnostic.cpp @@ -559,6 +559,9 @@ getLocationForCaller(const StackFrameContext *SFC, return PathDiagnosticLocation::createEnd(Dtor.getTriggerStmt(), SM, CallerCtx); } + case CFGElement::DeleteDtor: { + llvm_unreachable("not yet implemented!"); + } case CFGElement::BaseDtor: case CFGElement::MemberDtor: { const AnalysisDeclContext *CallerInfo = CallerCtx->getAnalysisDeclContext(); diff --git a/test/Analysis/cfg.cpp b/test/Analysis/cfg.cpp index 779e790102..243e1aed99 100644 --- a/test/Analysis/cfg.cpp +++ b/test/Analysis/cfg.cpp @@ -98,3 +98,49 @@ void testBuiltinSize() { extern int *dummy(); (void)__builtin_object_size(dummy(), 0); } + + +class A { +public: + A() {} + ~A() {} +}; + +// CHECK: [B2 (ENTRY)] +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B1] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: new A([B1.1]) +// CHECK-NEXT: 3: A *a = new A(); +// CHECK-NEXT: 4: a +// CHECK-NEXT: 5: [B1.4] (ImplicitCastExpr, LValueToRValue, class A *) +// CHECK-NEXT: 6: [B1.5]->~A() (Implicit destructor) +// CHECK-NEXT: 7: delete [B1.5] +// CHECK-NEXT: Preds (1): B2 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (1): B1 +void test_deletedtor() { + A *a = new A(); + delete a; +} + +// CHECK: [B2 (ENTRY)] +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B1] +// CHECK-NEXT: 1: 5 +// CHECK-NEXT: 2: (CXXConstructExpr, class A) +// CHECK-NEXT: 3: new A {{\[\[}}B1.1]] +// CHECK-NEXT: 4: A *a = new A [5]; +// CHECK-NEXT: 5: a +// CHECK-NEXT: 6: [B1.5] (ImplicitCastExpr, LValueToRValue, class A *) +// CHECK-NEXT: 7: [B1.6]->~A() (Implicit destructor) +// CHECK-NEXT: 8: delete [] [B1.6] +// CHECK-NEXT: Preds (1): B2 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (1): B1 +void test_deleteArraydtor() { + A *a = new A[5]; + delete[] a; +}