r326246 - [analyzer] Track temporaries without construction contexts for destruction.

Artem Dergachev via cfe-commits cfe-commits at lists.llvm.org
Tue Feb 27 13:02:58 PST 2018


Author: dergachev
Date: Tue Feb 27 13:02:58 2018
New Revision: 326246

URL: http://llvm.org/viewvc/llvm-project?rev=326246&view=rev
Log:
[analyzer] Track temporaries without construction contexts for destruction.

Sometimes it is not known at compile time which temporary objects will be
constructed, eg. 'x ? A() : B()' or 'C() || D()'. In this case we track which
temporary was constructed to know how to properly call the destructor.

Once the construction context for temporaries was introduced, we moved the
tracking code to the code that investigates the construction context.

Bring back the old mechanism because construction contexts are not always
available yet - eg. in the case where a temporary is constructed without a
constructor expression, eg. returned from a function by value. The mechanism
should still go away eventually.

Additionally, fix a bug in the temporary cleanup code for the case when
construction contexts are not available, which could lead to temporaries
staying in the program state and increasing memory consumption.

Differential Revision: https://reviews.llvm.org/D43666

Modified:
    cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
    cfe/trunk/lib/StaticAnalyzer/Core/ExprEngine.cpp
    cfe/trunk/test/Analysis/temporaries.cpp

Modified: cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h?rev=326246&r1=326245&r2=326246&view=diff
==============================================================================
--- cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h (original)
+++ cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h Tue Feb 27 13:02:58 2018
@@ -452,6 +452,10 @@ public:
                                        ExplodedNode *Pred,
                                        ExplodedNodeSet &Dst);
 
+  void VisitCXXBindTemporaryExpr(const CXXBindTemporaryExpr *BTE,
+                                 ExplodedNodeSet &PreVisit,
+                                 ExplodedNodeSet &Dst);
+
   void VisitCXXCatchStmt(const CXXCatchStmt *CS, ExplodedNode *Pred,
                          ExplodedNodeSet &Dst);
 

Modified: cfe/trunk/lib/StaticAnalyzer/Core/ExprEngine.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/ExprEngine.cpp?rev=326246&r1=326245&r2=326246&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Core/ExprEngine.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Core/ExprEngine.cpp Tue Feb 27 13:02:58 2018
@@ -1089,6 +1089,34 @@ void ExprEngine::processCleanupTemporary
   }
 }
 
+void ExprEngine::VisitCXXBindTemporaryExpr(const CXXBindTemporaryExpr *BTE,
+                                           ExplodedNodeSet &PreVisit,
+                                           ExplodedNodeSet &Dst) {
+  // This is a fallback solution in case we didn't have a construction
+  // context when we were constructing the temporary. Otherwise the map should
+  // have been populated there.
+  if (!getAnalysisManager().options.includeTemporaryDtorsInCFG()) {
+    // In case we don't have temporary destructors in the CFG, do not mark
+    // the initialization - we would otherwise never clean it up.
+    Dst = PreVisit;
+    return;
+  }
+  StmtNodeBuilder StmtBldr(PreVisit, Dst, *currBldrCtx);
+  for (ExplodedNode *Node : PreVisit) {
+    ProgramStateRef State = Node->getState();
+		const auto &Key = std::make_pair(BTE, Node->getStackFrame());
+
+    if (!State->contains<InitializedTemporaries>(Key)) {
+      // FIXME: Currently the state might also already contain the marker due to
+      // incorrect handling of temporaries bound to default parameters; for
+      // those, we currently skip the CXXBindTemporaryExpr but rely on adding
+      // temporary destructor nodes.
+      State = State->set<InitializedTemporaries>(Key, nullptr);
+    }
+    StmtBldr.generateNode(BTE, Node, State);
+  }
+}
+
 namespace {
 class CollectReachableSymbolsCallback final : public SymbolVisitor {
   InvalidatedSymbols Symbols;
@@ -1251,7 +1279,9 @@ void ExprEngine::Visit(const Stmt *S, Ex
       Bldr.takeNodes(Pred);
       ExplodedNodeSet PreVisit;
       getCheckerManager().runCheckersForPreStmt(PreVisit, Pred, S, *this);
-      getCheckerManager().runCheckersForPostStmt(Dst, PreVisit, S, *this);
+      ExplodedNodeSet Next;
+      VisitCXXBindTemporaryExpr(cast<CXXBindTemporaryExpr>(S), PreVisit, Next);
+      getCheckerManager().runCheckersForPostStmt(Dst, Next, S, *this);
       Bldr.addNodes(Dst);
       break;
     }
@@ -2194,13 +2224,13 @@ void ExprEngine::processEndOfFunction(No
                                        Pred->getLocationContext(),
                                        Pred->getStackFrame()->getParent()));
 
-  // FIXME: We currently assert that temporaries are clear, as lifetime extended
-  // temporaries are not always modelled correctly. In some cases when we
-  // materialize the temporary, we do createTemporaryRegionIfNeeded(), and
-  // the region changes, and also the respective destructor becomes automatic
-  // from temporary. So for now clean up the state manually before asserting.
-  // Ideally, the code above the assertion should go away, but the assertion
-  // should remain.
+  // FIXME: We currently cannot assert that temporaries are clear, because
+  // lifetime extended temporaries are not always modelled correctly. In some
+  // cases when we materialize the temporary, we do
+  // createTemporaryRegionIfNeeded(), and the region changes, and also the
+  // respective destructor becomes automatic from temporary. So for now clean up
+  // the state manually before asserting. Ideally, the code above the assertion
+  // should go away, but the assertion should remain.
   {
     ExplodedNodeSet CleanUpTemporaries;
     NodeBuilder Bldr(Pred, CleanUpTemporaries, BC);
@@ -2217,9 +2247,12 @@ void ExprEngine::processEndOfFunction(No
       LC = LC->getParent();
     }
     if (State != Pred->getState()) {
-      Bldr.generateNode(Pred->getLocation(), State, Pred);
-      assert(CleanUpTemporaries.size() <= 1);
-      Pred = CleanUpTemporaries.empty() ? Pred : *CleanUpTemporaries.begin();
+      Pred = Bldr.generateNode(Pred->getLocation(), State, Pred);
+      if (!Pred) {
+        // The node with clean temporaries already exists. We might have reached
+        // it on a path on which we initialize different temporaries.
+        return;
+      }
     }
   }
   assert(areInitializedTemporariesClear(Pred->getState(),

Modified: cfe/trunk/test/Analysis/temporaries.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/temporaries.cpp?rev=326246&r1=326245&r2=326246&view=diff
==============================================================================
--- cfe/trunk/test/Analysis/temporaries.cpp (original)
+++ cfe/trunk/test/Analysis/temporaries.cpp Tue Feb 27 13:02:58 2018
@@ -900,7 +900,13 @@ int glob;
 
 class C {
 public:
-  ~C() { glob = 1; }
+  ~C() {
+    glob = 1;
+    clang_analyzer_checkInlined(true);
+#ifdef TEMPORARY_DTORS
+    // expected-warning at -2{{TRUE}}
+#endif
+  }
 };
 
 C get();
@@ -913,12 +919,11 @@ void test(int coin) {
   // temporaries: the return value of get() and the elidable copy constructor
   // of that return value into is(). According to the CFG, we need to cleanup
   // both of them depending on whether the temporary corresponding to the
-  // return value of get() was initialized. However, for now we don't track
-  // temporaries returned from functions, so we take the wrong branch.
+  // return value of get() was initialized. However, we didn't track
+  // temporaries returned from functions, so we took the wrong branch.
   coin && is(get()); // no-crash
-  // FIXME: We should have called the destructor, i.e. should be TRUE,
-  // at least when we inline temporary destructors.
-  clang_analyzer_eval(glob == 1); // expected-warning{{UNKNOWN}}
+  // FIXME: Should be true once we inline all destructors.
+  clang_analyzer_eval(glob); // expected-warning{{UNKNOWN}}
 }
 } // namespace dont_forget_destructor_around_logical_op
 




More information about the cfe-commits mailing list