Do not inline temporary destructors for temporaries that bind to parameters.

http://reviews.llvm.org/D4740

Files:
  include/clang/Analysis/CFG.h
  lib/Analysis/CFG.cpp
  lib/Analysis/LiveVariables.cpp
  lib/StaticAnalyzer/Core/CallEvent.cpp
  lib/StaticAnalyzer/Core/ExprEngine.cpp
  lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
  lib/StaticAnalyzer/Core/ProgramState.cpp
  lib/StaticAnalyzer/Core/RegionStore.cpp
  test/Analysis/temporaries.cpp
Index: include/clang/Analysis/CFG.h
===================================================================
--- include/clang/Analysis/CFG.h
+++ include/clang/Analysis/CFG.h
@@ -279,14 +279,17 @@
 /// CFGTemporaryDtor - Represents C++ object destructor implicitly generated
 /// at the end of full expression for temporary object.
 class CFGTemporaryDtor : public CFGImplicitDtor {
+  static const bool* T;
 public:
-  CFGTemporaryDtor(CXXBindTemporaryExpr *expr)
-      : CFGImplicitDtor(TemporaryDtor, expr, nullptr) {}
+  CFGTemporaryDtor(CXXBindTemporaryExpr *expr, bool BindsParameter)
+      : CFGImplicitDtor(TemporaryDtor, expr, BindsParameter ? T : nullptr) {}
 
   const CXXBindTemporaryExpr *getBindTemporaryExpr() const {
     return static_cast<const CXXBindTemporaryExpr *>(Data1.getPointer());
   }
 
+  bool bindsParameter() const { return Data2.getPointer(); }
+
 private:
   friend class CFGElement;
   CFGTemporaryDtor() {}
@@ -676,8 +679,9 @@
     Elements.push_back(CFGMemberDtor(FD), C);
   }
 
-  void appendTemporaryDtor(CXXBindTemporaryExpr *E, BumpVectorContext &C) {
-    Elements.push_back(CFGTemporaryDtor(E), C);
+  void appendTemporaryDtor(CXXBindTemporaryExpr *E, BumpVectorContext &C,
+                           bool BindsParameter) {
+    Elements.push_back(CFGTemporaryDtor(E, BindsParameter), C);
   }
 
   void appendAutomaticObjDtor(VarDecl *VD, Stmt *S, BumpVectorContext &C) {
Index: lib/Analysis/CFG.cpp
===================================================================
--- lib/Analysis/CFG.cpp
+++ lib/Analysis/CFG.cpp
@@ -481,7 +481,8 @@
   CFGBlock *VisitBinaryOperatorForTemporaryDtors(BinaryOperator *E,
                                                  TempDtorContext &Context);
   CFGBlock *VisitCXXBindTemporaryExprForTemporaryDtors(
-      CXXBindTemporaryExpr *E, bool BindToTemporary, TempDtorContext &Context);
+      CXXBindTemporaryExpr *E, bool BindToTemporary, TempDtorContext &Context,
+      bool BindsParameter = false);
   CFGBlock *VisitConditionalOperatorForTemporaryDtors(
       AbstractConditionalOperator *E, bool BindToTemporary,
       TempDtorContext &Context);
@@ -537,8 +538,9 @@
   void appendMemberDtor(CFGBlock *B, FieldDecl *FD) {
     B->appendMemberDtor(FD, cfg->getBumpVectorContext());
   }
-  void appendTemporaryDtor(CFGBlock *B, CXXBindTemporaryExpr *E) {
-    B->appendTemporaryDtor(E, cfg->getBumpVectorContext());
+  void appendTemporaryDtor(CFGBlock *B, CXXBindTemporaryExpr *E,
+                           bool BindsParameter) {
+    B->appendTemporaryDtor(E, cfg->getBumpVectorContext(), BindsParameter);
   }
   void appendAutomaticObjDtor(CFGBlock *B, VarDecl *VD, Stmt *S) {
     B->appendAutomaticObjDtor(VD, S, cfg->getBumpVectorContext());
@@ -3611,6 +3613,22 @@
     case Stmt::CXXDefaultInitExprClass:
       E = cast<CXXDefaultInitExpr>(E)->getExpr();
       goto tryAgain;
+
+    case Stmt::CallExprClass: {
+      CFGBlock *B = Block;
+      for (Expr *Arg : cast<CallExpr>(E)->arguments()) {
+        if (!Arg) continue;
+        if (CXXBindTemporaryExpr *BTE = dyn_cast<CXXBindTemporaryExpr>(Arg)) {
+          if (CFGBlock *R = VisitCXXBindTemporaryExprForTemporaryDtors(
+                  BTE, false, Context, /*BindsParameter=*/true)) {
+            B = R;
+          }
+        } else if (CFGBlock *R = VisitForTemporaryDtors(Arg, false, Context)) {
+          B = R;
+        }
+      }
+      return B;
+    }
   }
 }
 
@@ -3670,7 +3688,8 @@
 }
 
 CFGBlock *CFGBuilder::VisitCXXBindTemporaryExprForTemporaryDtors(
-    CXXBindTemporaryExpr *E, bool BindToTemporary, TempDtorContext &Context) {
+    CXXBindTemporaryExpr *E, bool BindToTemporary, TempDtorContext &Context,
+    bool BindsParameter) {
   // First add destructors for temporaries in subexpression.
   CFGBlock *B = VisitForTemporaryDtors(E->getSubExpr(), false, Context);
   if (!BindToTemporary) {
@@ -3697,7 +3716,7 @@
     if (Context.needsTempDtorBranch()) {
       Context.setDecisionPoint(Succ, E);
     }
-    appendTemporaryDtor(Block, E);
+    appendTemporaryDtor(Block, E, BindsParameter);
 
     B = Block;
   }
@@ -3753,6 +3772,8 @@
 
 } // end anonymous namespace
 
+const bool *CFGTemporaryDtor::T = new bool(true);
+
 /// createBlock - Constructs and adds a new CFGBlock to the CFG.  The block has
 ///  no successors or predecessors.  If this is the first block created in the
 ///  CFG, it is automatically set to be the Entry and Exit of the CFG.
Index: lib/Analysis/LiveVariables.cpp
===================================================================
--- lib/Analysis/LiveVariables.cpp
+++ lib/Analysis/LiveVariables.cpp
@@ -409,6 +409,13 @@
       val.liveDecls = DSetFact.add(val.liveDecls, Dtor->getVarDecl());
       continue;
     }
+    if (Optional<CFGTemporaryDtor> Dtor =
+            elem.getAs<CFGTemporaryDtor>()) {
+      // Temporary objects need to survive until the destructor is called.
+      val.liveStmts = SSetFact.add(val.liveStmts,
+                                   Dtor->getBindTemporaryExpr()->getSubExpr());
+      continue;
+    }
 
     if (!elem.getAs<CFGStmt>())
       continue;
Index: lib/StaticAnalyzer/Core/CallEvent.cpp
===================================================================
--- lib/StaticAnalyzer/Core/CallEvent.cpp
+++ lib/StaticAnalyzer/Core/CallEvent.cpp
@@ -959,7 +959,6 @@
   CFGElement E = (*B)[CalleeCtx->getIndex()];
   assert(E.getAs<CFGImplicitDtor>() &&
          "All other CFG elements should have exprs");
-  assert(!E.getAs<CFGTemporaryDtor>() && "We don't handle temporaries yet");
 
   SValBuilder &SVB = State->getStateManager().getSValBuilder();
   const CXXDestructorDecl *Dtor = cast<CXXDestructorDecl>(CalleeCtx->getDecl());
Index: lib/StaticAnalyzer/Core/ExprEngine.cpp
===================================================================
--- lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -676,13 +676,23 @@
   State = State->remove<InitializedTemporariesSet>(
       std::make_pair(D.getBindTemporaryExpr(), Pred->getStackFrame()));
   StmtBldr.generateNode(D.getBindTemporaryExpr(), Pred, State);
-
-  QualType varType = D.getBindTemporaryExpr()->getSubExpr()->getType();
   assert(CleanDtorState.size() == 1);
   ExplodedNode *CleanPred = *CleanDtorState.begin();
-  // FIXME: Inlining of temporary destructors is not supported yet anyway, so
-  // we just put a NULL region for now. This will need to be changed later.
-  VisitCXXDestructor(varType, nullptr, D.getBindTemporaryExpr(),
+
+  QualType varType = D.getBindTemporaryExpr()->getSubExpr()->getType();
+
+  const LocationContext *LCtx = CleanPred->getLocationContext();
+  SVal Val = CleanPred->getState()->getSVal(
+      D.getBindTemporaryExpr()->getSubExpr(), LCtx->getCurrentStackFrame());
+  const MemRegion *Region = nullptr;
+  // If the class does not have any members, there will not be a region
+  // for it bound in the environment.
+  if (Optional<nonloc::LazyCompoundVal> LCV =
+          Val.getAs<nonloc::LazyCompoundVal>()) {
+    Region = LCV->getRegion();
+  }
+
+  VisitCXXDestructor(varType, Region, D.getBindTemporaryExpr(),
                      /*IsBase=*/false, CleanPred, Dst);
 }
 
Index: lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
===================================================================
--- lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
+++ lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
@@ -637,12 +637,6 @@
     if (!Opts.mayInlineCXXMemberFunction(CIMK_Destructors))
       return CIP_DisallowedAlways;
 
-    // FIXME: This is a hack. We don't handle temporary destructors
-    // right now, so we shouldn't inline their constructors.
-    if (CtorExpr->getConstructionKind() == CXXConstructExpr::CK_Complete)
-      if (!Target || !isa<DeclRegion>(Target))
-        return CIP_DisallowedOnce;
-
     break;
   }
   case CE_CXXDestructor: {
@@ -807,12 +801,15 @@
   AnalysisDeclContextManager &ADCMgr = AMgr.getAnalysisDeclContextManager();
   AnalysisDeclContext *CalleeADC = ADCMgr.getContext(D);
 
-  // Temporary object destructor processing is currently broken, so we never
-  // inline them.
-  // FIXME: Remove this once temp destructors are working.
   if (isa<CXXDestructorCall>(Call)) {
-    if ((*currBldrCtx->getBlock())[currStmtIdx].getAs<CFGTemporaryDtor>())
-      return false;
+    if (Optional<CFGTemporaryDtor> Dtor =
+            (*currBldrCtx->getBlock())[currStmtIdx].getAs<CFGTemporaryDtor>()) {
+      // We must not inline temporary destructors for temporaries that are
+      // bound to parameters, as we currently don't support correctly
+      // invalidating our knowledge about them when they escape.
+      if (Dtor->bindsParameter())
+        return false;
+    }
   }
 
   // The auto-synthesized bodies are essential to inline as they are
Index: lib/StaticAnalyzer/Core/ProgramState.cpp
===================================================================
--- lib/StaticAnalyzer/Core/ProgramState.cpp
+++ lib/StaticAnalyzer/Core/ProgramState.cpp
@@ -506,16 +506,7 @@
 }
 
 bool ScanReachableSymbols::scan(nonloc::LazyCompoundVal val) {
-  bool wasVisited = !visited.insert(val.getCVData()).second;
-  if (wasVisited)
-    return true;
-
-  StoreManager &StoreMgr = state->getStateManager().getStoreManager();
-  // FIXME: We don't really want to use getBaseRegion() here because pointer
-  // arithmetic doesn't apply, but scanReachableSymbols only accepts base
-  // regions right now.
-  const MemRegion *R = val.getRegion()->getBaseRegion();
-  return StoreMgr.scanReachableSymbols(val.getStore(), R, *this);
+  return scan(val.getRegion());
 }
 
 bool ScanReachableSymbols::scan(nonloc::CompoundVal val) {
Index: lib/StaticAnalyzer/Core/RegionStore.cpp
===================================================================
--- lib/StaticAnalyzer/Core/RegionStore.cpp
+++ lib/StaticAnalyzer/Core/RegionStore.cpp
@@ -1899,8 +1899,9 @@
     QualType Ty = TR->getValueType();
     if (Ty->isArrayType())
       return bindArray(B, TR, V);
-    if (Ty->isStructureOrClassType())
+    if (Ty->isStructureOrClassType()) {
       return bindStruct(B, TR, V);
+    }
     if (Ty->isVectorType())
       return bindVector(B, TR, V);
     if (Ty->isUnionType())
@@ -2110,8 +2111,9 @@
   // Handle lazy compound values and symbolic values.
   if (Optional<nonloc::LazyCompoundVal> LCV =
         V.getAs<nonloc::LazyCompoundVal>()) {
-    if (Optional<RegionBindingsRef> NewB = tryBindSmallStruct(B, R, RD, *LCV))
+    if (Optional<RegionBindingsRef> NewB = tryBindSmallStruct(B, R, RD, *LCV)) {
       return *NewB;
+    }
     return bindAggregate(B, R, V);
   }
   if (V.getAs<nonloc::SymbolVal>())
Index: test/Analysis/temporaries.cpp
===================================================================
--- test/Analysis/temporaries.cpp
+++ test/Analysis/temporaries.cpp
@@ -104,9 +104,7 @@
 #if __cplusplus >= 201103L
     clang_analyzer_eval(((HasCtor){1, 42}).y == 42); // expected-warning{{TRUE}}
 
-    // FIXME: should be TRUE, but we don't inline the constructors of
-    // temporaries because we can't model their destructors yet.
-    clang_analyzer_eval(((HasCtorDtor){1, 42}).y == 42); // expected-warning{{UNKNOWN}}
+    clang_analyzer_eval(((HasCtorDtor){1, 42}).y == 42); // expected-warning{{TRUE}}
 #endif
   }
 }
@@ -347,6 +345,49 @@
     }
   }
 
+  struct NoWarnDerefDtor {
+    NoWarnDerefDtor(int *p) : p(p) {}
+    ~NoWarnDerefDtor() { *p = 23; } // no warning
+    int *p;
+  };
+  void testDtorInlining() {
+    int x;
+    (NoWarnDerefDtor(&x));
+    clang_analyzer_eval(x == 23); // expected-warning{{TRUE}}
+  }
+  void use(NoWarnDerefDtor);
+  void testArgumentDtorInliningPreventedByValue() {
+    int x;
+    NoWarnDerefDtor p(nullptr);
+    use(p);
+    p.p = &x;
+  }
+
+  struct WarnDerefDtor {
+    WarnDerefDtor(int *p) : p(p) {}
+    ~WarnDerefDtor() {
+      *p = 23; // expected-warning{{Dereference of null pointer}}
+    }
+    int *p;
+  };
+  void useRef(WarnDerefDtor& p) {
+    p.p = nullptr;
+  }
+  void testArgumentDtorInliningWorksByReference() {
+    int x;
+    WarnDerefDtor p(&x);
+    useRef(p);
+  }
+  void useConstRef(const NoWarnDerefDtor& p) {
+    const_cast<NoWarnDerefDtor&>(p).p = nullptr;
+  }
+  void testArgumentDtorInliningWorksByConstReference() {
+    int x;
+    // FIXME: This should warn. We need to implement correct handling of
+    // temporaries bound to parameters first.
+    useConstRef(NoWarnDerefDtor(&x));
+  }
+
   void testIfAtEndOfLoop() {
     int y = 0;
     while (true) {
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to