[cfe-dev] lifetime markers
Daniel Marjamäki
daniel.marjamaki at gmail.com
Fri Jun 27 06:27:56 PDT 2014
Hello!
We are working on inserting lifetime markers in the CFG. As described here:
http://lists.cs.uiuc.edu/pipermail/cfe-dev/2014-April/036397.html
I need advice.
Is the idea that we will add a visitor in RecursiveASTVisitor that is
called when an outofscope marker is seen? Then, could you say a little
about how I can do that? Or should we make something like the
LiveVariables approach or should the checkers parse the
CFG/ExplodedNode and search for these markers?
The attached patch is a proof-of-concept patch that inserts lifetime
markers in the cfg builder and writes debug messages when they are
later seen in the exprengine.
Best regards,
Daniel Marjamäki
-------------- next part --------------
Index: lib/Analysis/CFG.cpp
===================================================================
--- lib/Analysis/CFG.cpp (revision 211894)
+++ lib/Analysis/CFG.cpp (working copy)
@@ -27,6 +27,7 @@
#include "llvm/Support/Format.h"
#include "llvm/Support/GraphWriter.h"
#include "llvm/Support/SaveAndRestore.h"
+#include <iostream>
using namespace clang;
@@ -464,6 +465,9 @@
void appendNewAllocator(CFGBlock *B, CXXNewExpr *NE) {
B->appendNewAllocator(NE, cfg->getBumpVectorContext());
}
+ void appendOutOfScope(CFGBlock *B, const VarDecl *S) {
+ B->appendOutOfScope(S, cfg->getBumpVectorContext());
+ }
void appendBaseDtor(CFGBlock *B, const CXXBaseSpecifier *BS) {
B->appendBaseDtor(BS, cfg->getBumpVectorContext());
}
@@ -905,7 +909,6 @@
assert(cfg.get());
if (!Statement)
return nullptr;
-
// Create an empty block that will serve as the exit block for the CFG. Since
// this is the first block added to the CFG, it will be implicitly registered
// as the exit block.
@@ -917,6 +920,17 @@
if (const CXXDestructorDecl *DD = dyn_cast_or_null<CXXDestructorDecl>(D))
addImplicitDtorsForDestructor(DD);
+ if (const FunctionDecl *DD = dyn_cast_or_null<FunctionDecl>(D)) {
+ LocalScope *Scope = 0;
+ for (auto *DI : DD->params()) {
+ if (VarDecl *VD = dyn_cast<VarDecl>(DI)) {
+ Scope = addLocalScopeForVarDecl(VD, Scope);
+ std::cout << "debug: Insert OutOfScope" << std::endl;
+ autoCreateBlock();
+ appendOutOfScope(Block, VD);
+ }
+ }
+ }
// Visit the statements and create the CFG.
CFGBlock *B = addStmt(Statement);
@@ -1210,8 +1224,12 @@
return Scope;
for (auto *DI : DS->decls())
- if (VarDecl *VD = dyn_cast<VarDecl>(DI))
+ if (VarDecl *VD = dyn_cast<VarDecl>(DI)) {
Scope = addLocalScopeForVarDecl(VD, Scope);
+ std::cout << "debug: Insert OutOfScope" << std::endl;
+ autoCreateBlock();
+ appendOutOfScope(Block, VD);
+ }
return Scope;
}
@@ -1476,6 +1494,9 @@
case Stmt::WhileStmtClass:
return VisitWhileStmt(cast<WhileStmt>(S));
+ case Stmt::FunctionParmPackExprClass:
+ return VisitWhileStmt(cast<WhileStmt>(S));
+
}
}
@@ -2042,6 +2063,9 @@
LocalScope::const_iterator BeginScopePos = ScopePos;
addLocalScopeForVarDecl(VD);
addAutomaticObjDtors(ScopePos, BeginScopePos, I);
+
+ std::cout << "debug: Insert OutOfScope" << std::endl;
+ appendOutOfScope(Block, VD);
}
// The block we were processing is now finished. Make it the successor
@@ -2248,8 +2272,11 @@
addLocalScopeForStmt(Init);
LocalScope::const_iterator LoopBeginScopePos = ScopePos;
- if (VarDecl *VD = F->getConditionVariable())
+ if (VarDecl *VD = F->getConditionVariable()) {
addLocalScopeForVarDecl(VD);
+ std::cout << "debug: Insert OutOfScope" << std::endl;
+ appendOutOfScope(Block, VD);
+ }
LocalScope::const_iterator ContinueScopePos = ScopePos;
addAutomaticObjDtors(ScopePos, save_scope_pos.get(), F);
@@ -2592,6 +2619,8 @@
if (VarDecl *VD = W->getConditionVariable()) {
addLocalScopeForVarDecl(VD);
addAutomaticObjDtors(ScopePos, LoopBeginScopePos, W);
+ std::cout << "debug: Insert OutOfScope" << std::endl;
+ appendOutOfScope(Block, VD);
}
// "while" is a control-flow statement. Thus we stop processing the current
@@ -2921,6 +2950,8 @@
LocalScope::const_iterator SwitchBeginScopePos = ScopePos;
addLocalScopeForVarDecl(VD);
addAutomaticObjDtors(ScopePos, SwitchBeginScopePos, Terminator);
+ std::cout << "debug: Insert OutOfScope" << std::endl;
+ appendOutOfScope(Block, VD);
}
if (Block) {
@@ -3204,6 +3235,8 @@
LocalScope::const_iterator BeginScopePos = ScopePos;
addLocalScopeForVarDecl(VD);
addAutomaticObjDtors(ScopePos, BeginScopePos, CS);
+ std::cout << "debug: Insert OutOfScope" << std::endl;
+ appendOutOfScope(Block, VD);
}
if (CS->getHandlerBlock())
@@ -3707,6 +3740,7 @@
case CFGElement::Statement:
case CFGElement::Initializer:
case CFGElement::NewAllocator:
+ case CFGElement::OutOfScope:
llvm_unreachable("getDestructorDecl should only be used with "
"ImplicitDtors");
case CFGElement::AutomaticObjectDtor: {
@@ -4138,6 +4172,16 @@
OS << "~";
BT->getType().print(OS, PrintingPolicy(Helper.getLangOpts()));
OS << "() (Temporary object destructor)\n";
+ } else if (Optional<CFGOutOfScope> TE = E.getAs<CFGOutOfScope>()) {
+ const VarDecl *VD = TE->getVarDecl();
+ Helper.handleDecl(VD, OS);
+
+ const Type* T = VD->getType().getTypePtr();
+ if (const ReferenceType* RT = T->getAs<ReferenceType>())
+ T = RT->getPointeeType().getTypePtr();
+ T = T->getBaseElementTypeUnsafe();
+
+ OS << " (Out of scope)\n";
}
}
Index: lib/Analysis/Consumed.cpp
===================================================================
--- lib/Analysis/Consumed.cpp (revision 211894)
+++ lib/Analysis/Consumed.cpp (working copy)
@@ -1417,6 +1417,12 @@
break;
}
+ /*case CFGElement::OutOfScope: {
+ Visitor.Visit(B.castAs<CFGOutOfScope>().getStmt());
+
+ break;
+ }*/
+
default:
break;
}
Index: lib/StaticAnalyzer/Core/PathDiagnostic.cpp
===================================================================
--- lib/StaticAnalyzer/Core/PathDiagnostic.cpp (revision 211894)
+++ lib/StaticAnalyzer/Core/PathDiagnostic.cpp (working copy)
@@ -571,6 +571,7 @@
}
case CFGElement::TemporaryDtor:
case CFGElement::NewAllocator:
+ case CFGElement::OutOfScope:
llvm_unreachable("not yet implemented!");
}
Index: lib/StaticAnalyzer/Core/ExprEngine.cpp
===================================================================
--- lib/StaticAnalyzer/Core/ExprEngine.cpp (revision 211894)
+++ lib/StaticAnalyzer/Core/ExprEngine.cpp (working copy)
@@ -29,6 +29,7 @@
#include "llvm/ADT/ImmutableList.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/Support/raw_ostream.h"
+#include <iostream>
#ifndef NDEBUG
#include "llvm/Support/GraphWriter.h"
@@ -292,6 +293,10 @@
ProcessNewAllocator(E.castAs<CFGNewAllocator>().getAllocatorExpr(),
Pred);
return;
+ case CFGElement::OutOfScope:
+ std::cout << "debug: ExprEngine: OutOfScope" << std::endl;
+ ProcessOutOfScope(E.castAs<CFGOutOfScope>(), Pred);
+ return;
case CFGElement::AutomaticObjectDtor:
case CFGElement::DeleteDtor:
case CFGElement::BaseDtor:
@@ -526,6 +531,12 @@
Engine.enqueue(Dst, currBldrCtx->getBlock(), currStmtIdx);
}
+void ExprEngine::ProcessOutOfScope(const CFGOutOfScope Dtor,
+ ExplodedNode *Pred) {
+ const VarDecl *varDecl = Dtor.getVarDecl();
+ VisitOutOfScope(varDecl, Pred);
+}
+
void ExprEngine::ProcessImplicitDtor(const CFGImplicitDtor D,
ExplodedNode *Pred) {
ExplodedNodeSet Dst;
@@ -1802,6 +1813,10 @@
}
}
+void ExprEngine::VisitOutOfScope(const VarDecl *S, ExplodedNode *Pred) {
+ std::cout << "debug: ExprEngine: VisitOutOfScope" << std::endl;
+}
+
/// VisitMemberExpr - Transfer function for member expressions.
void ExprEngine::VisitMemberExpr(const MemberExpr *M, ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
Index: include/clang/Analysis/CFG.h
===================================================================
--- include/clang/Analysis/CFG.h (revision 211894)
+++ include/clang/Analysis/CFG.h (working copy)
@@ -57,6 +57,8 @@
Statement,
Initializer,
NewAllocator,
+ // variable going out of scope
+ OutOfScope,
// dtor kind
AutomaticObjectDtor,
DeleteDtor,
@@ -166,6 +168,24 @@
}
};
+/// CFGOutOfScope - Represents variable out of scope.
+class CFGOutOfScope : public CFGElement {
+public:
+ CFGOutOfScope(VarDecl *S) : CFGElement(OutOfScope, S) {}
+ explicit CFGOutOfScope(const VarDecl *S) : CFGElement(OutOfScope, S) {}
+
+ const VarDecl *getVarDecl() const {
+ return static_cast<const VarDecl*>(Data1.getPointer());
+ }
+
+private:
+ friend class CFGElement;
+ CFGOutOfScope() {}
+ static bool isKind(const CFGElement &elem) {
+ return elem.getKind() == OutOfScope;
+ }
+};
+
/// CFGImplicitDtor - Represents C++ object destructor implicitly generated
/// by compiler on various occasions.
class CFGImplicitDtor : public CFGElement {
@@ -668,6 +688,10 @@
Elements.push_back(CFGNewAllocator(NE), C);
}
+ void appendOutOfScope(const VarDecl *S, BumpVectorContext &C) {
+ Elements.push_back(CFGOutOfScope(S), C);
+ }
+
void appendBaseDtor(const CXXBaseSpecifier *BS, BumpVectorContext &C) {
Elements.push_back(CFGBaseDtor(BS), C);
}
Index: include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
===================================================================
--- include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h (revision 211894)
+++ include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h (working copy)
@@ -198,6 +198,8 @@
void ProcessInitializer(const CFGInitializer I, ExplodedNode *Pred);
+ void ProcessOutOfScope(const CFGOutOfScope D, ExplodedNode *Pred);
+
void ProcessImplicitDtor(const CFGImplicitDtor D, ExplodedNode *Pred);
void ProcessNewAllocator(const CXXNewExpr *NE, ExplodedNode *Pred);
@@ -437,6 +439,8 @@
ExplodedNode *Pred,
ExplodedNodeSet &Dst);
+ void VisitOutOfScope(const VarDecl *S, ExplodedNode *Pred);
+
/// evalEagerlyAssumeBinOpBifurcation - Given the nodes in 'Src', eagerly assume symbolic
/// expressions of the form 'x != 0' and generate new nodes (stored in Dst)
/// with those assumptions.
More information about the cfe-dev
mailing list