[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