Dumping of intermediate state developed with Jordan...

http://reviews.llvm.org/D4740

Files:
  include/clang/Analysis/CFG.h
  include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h
  lib/Analysis/CFG.cpp
  lib/Analysis/LiveVariables.cpp
  lib/StaticAnalyzer/Core/CallEvent.cpp
  lib/StaticAnalyzer/Core/CoreEngine.cpp
  lib/StaticAnalyzer/Core/ExprEngine.cpp
  lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
  lib/StaticAnalyzer/Core/RegionStore.cpp
  test/Analysis/auto-obj-dtors-cfg-output.cpp
  test/Analysis/cfg.cpp
  test/Analysis/temp-obj-dtors-cfg-output.cpp
  test/Analysis/temporaries.cpp
Index: include/clang/Analysis/CFG.h
===================================================================
--- include/clang/Analysis/CFG.h
+++ include/clang/Analysis/CFG.h
@@ -280,13 +280,15 @@
 /// at the end of full expression for temporary object.
 class CFGTemporaryDtor : public CFGImplicitDtor {
 public:
-  CFGTemporaryDtor(CXXBindTemporaryExpr *expr)
-      : CFGImplicitDtor(TemporaryDtor, expr, nullptr) {}
+  CFGTemporaryDtor(CXXBindTemporaryExpr *expr, bool BindsParameter)
+      : CFGImplicitDtor(TemporaryDtor, expr, BindsParameter ? this : 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 +678,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: include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h
===================================================================
--- include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h
+++ include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h
@@ -290,7 +290,8 @@
 
   static inline bool isLocType(QualType T) {
     return T->isAnyPointerType() || T->isBlockPointerType() || 
-           T->isReferenceType() || T->isNullPtrType();
+           T->isReferenceType() || T->isNullPtrType() ||
+           T->isRecordType();
   }
 
 private:
Index: lib/Analysis/CFG.cpp
===================================================================
--- lib/Analysis/CFG.cpp
+++ lib/Analysis/CFG.cpp
@@ -484,7 +484,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);
@@ -505,6 +506,9 @@
     return Visit(S, AddStmtChoice::AlwaysAdd);
   }
   CFGBlock *addInitializer(CXXCtorInitializer *I);
+  void addAutomaticObjDtorsForLifetimeExtendedTemporaries(
+      Stmt *E, bool LifetimeExtended = false);
+  void addAutomaticObjDtorsForVarDecl(VarDecl *VD, Stmt* S);
   void addAutomaticObjDtors(LocalScope::const_iterator B,
                             LocalScope::const_iterator E, Stmt *S);
   void addImplicitDtorsForDestructor(const CXXDestructorDecl *DD);
@@ -515,6 +519,9 @@
   void addLocalScopeForStmt(Stmt *S);
   LocalScope* addLocalScopeForDeclStmt(DeclStmt *DS,
                                        LocalScope* Scope = nullptr);
+  bool needsLocalScopeForLifetimeExtendedTemporary(const Stmt* E);
+  bool needsLocalScopeForVarDecl(const VarDecl* VD);
+  bool needsLocalScopeForType(QualType QT);
   LocalScope* addLocalScopeForVarDecl(VarDecl *VD, LocalScope* Scope = nullptr);
 
   void addLocalScopeAndDtors(Stmt *S);
@@ -540,8 +547,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());
@@ -1101,52 +1109,87 @@
   return Block;
 }
 
-/// \brief Retrieve the type of the temporary object whose lifetime was 
-/// extended by a local reference with the given initializer.
-static QualType getReferenceInitTemporaryType(ASTContext &Context,
-                                              const Expr *Init) {
-  while (true) {
-    // Skip parentheses.
-    Init = Init->IgnoreParens();
-    
-    // Skip through cleanups.
-    if (const ExprWithCleanups *EWC = dyn_cast<ExprWithCleanups>(Init)) {
-      Init = EWC->getSubExpr();
-      continue;
-    }
-    
-    // Skip through the temporary-materialization expression.
-    if (const MaterializeTemporaryExpr *MTE
-          = dyn_cast<MaterializeTemporaryExpr>(Init)) {
-      Init = MTE->GetTemporaryExpr();
-      continue;
+void CFGBuilder::addAutomaticObjDtorsForLifetimeExtendedTemporaries(
+    Stmt *E, bool LifetimeExtended) {
+  switch (E->getStmtClass()) {
+    // FIXME: What do we want to do for conditional operators and binary
+    // conditional operators?
+  case Stmt::MaterializeTemporaryExprClass: {
+    const MaterializeTemporaryExpr * MTE = cast<MaterializeTemporaryExpr>(E);
+    if (MTE->getStorageDuration() != SD_FullExpression) {
+      SmallVector<const Expr *, 2> CommaLHSs;
+      SmallVector<SubobjectAdjustment, 2> Adjustments;
+      assert(MTE->GetTemporaryExpr());
+      const Expr *ExtendedE =
+          MTE->GetTemporaryExpr()->skipRValueSubobjectAdjustments(CommaLHSs,
+                                                                  Adjustments);
+      addAutomaticObjDtorsForLifetimeExtendedTemporaries(
+          const_cast<Expr*>(ExtendedE), /*LifetimeExtended=*/true);
     }
-    
-    // Skip derived-to-base and no-op casts.
-    if (const CastExpr *CE = dyn_cast<CastExpr>(Init)) {
-      if ((CE->getCastKind() == CK_DerivedToBase ||
-           CE->getCastKind() == CK_UncheckedDerivedToBase ||
-           CE->getCastKind() == CK_NoOp) &&
-          Init->getType()->isRecordType()) {
-        Init = CE->getSubExpr();
-        continue;
+  } break;
+  case Stmt::CXXBindTemporaryExprClass:
+    // If the temporary is not lifetime extended, we'll handle it in
+    // VisitForTemporaryDtors instead.
+    if (LifetimeExtended) {
+      const CXXDestructorDecl *Dtor =
+          cast<CXXBindTemporaryExpr>(E)->getTemporary()->getDestructor();
+      if (Dtor->isNoReturn()) {
+        Block = createNoReturnBlock();
+      } else {
+        autoCreateBlock();
       }
+
+      appendTemporaryDtor(Block, cast<CXXBindTemporaryExpr>(E), false);
     }
-    
-    // Skip member accesses into rvalues.
-    if (const MemberExpr *ME = dyn_cast<MemberExpr>(Init)) {
-      if (!ME->isArrow() && ME->getBase()->isRValue()) {
-        Init = ME->getBase();
-        continue;
-      }
+    break;
+  case Stmt::ConditionalOperatorClass:
+  case Stmt::BinaryConditionalOperatorClass:
+    // FIXME: Implement. If both the true and false branch yield lifetime
+    // extended temporaries, we need to add a branch that triggers on which
+    // temporary constructor was executed.
+    break;
+  default:
+    LifetimeExtended = false;
+    // Fallthrough.
+  case Stmt::ParenExprClass:
+  case Stmt::CXXFunctionalCastExprClass:
+  case Stmt::ImplicitCastExprClass:
+  case Stmt::ExprWithCleanupsClass:
+  case Stmt::CXXDefaultArgExprClass:
+  case Stmt::CXXDefaultInitExprClass:
+    for (Stmt *Child : E->children()) {
+      if (Child)
+        addAutomaticObjDtorsForLifetimeExtendedTemporaries(Child,
+                                                           LifetimeExtended);
     }
-    
+    break;
+  case Stmt::BlockExprClass:
+  case Stmt::LambdaExprClass:
     break;
   }
+}
+
+void CFGBuilder::addAutomaticObjDtorsForVarDecl(VarDecl *VD,
+                                                Stmt *S) {
+  if (!VD->getType()->isReferenceType() &&
+      needsLocalScopeForType(VD->getType())) {
+    QualType QT = VD->getType();
+    QT = Context->getBaseElementType(QT);
 
-  return Init->getType();
+    const CXXDestructorDecl *Dtor = QT->getAsCXXRecordDecl()->getDestructor();
+    if (Dtor->isNoReturn())
+      Block = createNoReturnBlock();
+    else
+      autoCreateBlock();
+
+    appendAutomaticObjDtor(Block, VD, S);
+  }
+  if (const Expr *Init = VD->getInit()) {
+    addAutomaticObjDtorsForLifetimeExtendedTemporaries(
+        const_cast<Expr *>(Init));
+  }
 }
-  
+
 /// addAutomaticObjDtors - Add to current block automatic objects destructors
 /// for objects in range of local scope positions. Use S as trigger statement
 /// for destructors.
@@ -1170,22 +1213,7 @@
   for (SmallVectorImpl<VarDecl*>::reverse_iterator I = Decls.rbegin(),
                                                    E = Decls.rend();
        I != E; ++I) {
-    // If this destructor is marked as a no-return destructor, we need to
-    // create a new block for the destructor which does not have as a successor
-    // anything built thus far: control won't flow out of this block.
-    QualType Ty = (*I)->getType();
-    if (Ty->isReferenceType()) {
-      Ty = getReferenceInitTemporaryType(*Context, (*I)->getInit());
-    }
-    Ty = Context->getBaseElementType(Ty);
-
-    const CXXDestructorDecl *Dtor = Ty->getAsCXXRecordDecl()->getDestructor();
-    if (Dtor->isNoReturn())
-      Block = createNoReturnBlock();
-    else
-      autoCreateBlock();
-
-    appendAutomaticObjDtor(Block, *I, S);
+    addAutomaticObjDtorsForVarDecl(*I, S);
   }
 }
 
@@ -1283,6 +1311,58 @@
   return Scope;
 }
 
+bool CFGBuilder::needsLocalScopeForType(QualType QT) {
+  // Check for constant size array. Set type to array element type.
+  while (const ConstantArrayType *AT = Context->getAsConstantArrayType(QT)) {
+    if (AT->getSize() == 0)
+      return false;
+    QT = AT->getElementType();
+  }
+
+  // Check if type is a C++ class with non-trivial destructor.
+  if (const CXXRecordDecl *CD = QT->getAsCXXRecordDecl())
+    return !CD->hasTrivialDestructor();
+  return false;
+}
+
+bool CFGBuilder::needsLocalScopeForLifetimeExtendedTemporary(const Stmt* E) {
+  // FIXME: Handle cases where the scope would be introduced by a nested
+  // lambda or block.
+  switch (E->getStmtClass()) {
+  case Stmt::MaterializeTemporaryExprClass: {
+    const MaterializeTemporaryExpr * MTE = cast<MaterializeTemporaryExpr>(E);
+    if (MTE->getStorageDuration() != SD_FullExpression) {
+      SmallVector<const Expr *, 2> CommaLHSs;
+      SmallVector<SubobjectAdjustment, 2> Adjustments;
+      assert(MTE->GetTemporaryExpr());
+      const Expr *ExtendedE =
+          MTE->GetTemporaryExpr()->skipRValueSubobjectAdjustments(CommaLHSs,
+                                                                  Adjustments);
+      return needsLocalScopeForType(ExtendedE->getType()) ||
+             needsLocalScopeForLifetimeExtendedTemporary(ExtendedE);
+    }
+  } break;
+  default:
+    for (const Stmt *Child : E->children()) {
+      if (Child && needsLocalScopeForLifetimeExtendedTemporary(Child))
+        return true;
+    }
+    break;
+  }
+  return false;
+}
+
+bool CFGBuilder::needsLocalScopeForVarDecl(const VarDecl* VD) {
+  if (!VD->getType()->isReferenceType() &&
+      needsLocalScopeForType(VD->getType())) {
+    return true;
+  }
+  if (const Expr *Init = VD->getInit()) {
+    return needsLocalScopeForLifetimeExtendedTemporary(Init);
+  }
+  return false;
+}
+
 /// addLocalScopeForVarDecl - Add LocalScope for variable declaration. It will
 /// create add scope for automatic objects and temporary objects bound to
 /// const reference. Will reuse Scope if not NULL.
@@ -1300,43 +1380,11 @@
   default: return Scope;
   }
 
-  // Check for const references bound to temporary. Set type to pointee.
-  QualType QT = VD->getType();
-  if (QT.getTypePtr()->isReferenceType()) {
-    // Attempt to determine whether this declaration lifetime-extends a
-    // temporary.
-    //
-    // FIXME: This is incorrect. Non-reference declarations can lifetime-extend
-    // temporaries, and a single declaration can extend multiple temporaries.
-    // We should look at the storage duration on each nested
-    // MaterializeTemporaryExpr instead.
-    const Expr *Init = VD->getInit();
-    if (!Init)
-      return Scope;
-    if (const ExprWithCleanups *EWC = dyn_cast<ExprWithCleanups>(Init))
-      Init = EWC->getSubExpr();
-    if (!isa<MaterializeTemporaryExpr>(Init))
-      return Scope;
-
-    // Lifetime-extending a temporary.
-    QT = getReferenceInitTemporaryType(*Context, Init);
-  }
-
-  // Check for constant size array. Set type to array element type.
-  while (const ConstantArrayType *AT = Context->getAsConstantArrayType(QT)) {
-    if (AT->getSize() == 0)
-      return Scope;
-    QT = AT->getElementType();
-  }
-
-  // Check if type is a C++ class with non-trivial destructor.
-  if (const CXXRecordDecl *CD = QT->getAsCXXRecordDecl())
-    if (!CD->hasTrivialDestructor()) {
-      // Add the variable to scope
-      Scope = createOrReuseLocalScope(Scope);
-      Scope->addVar(VD);
-      ScopePos = Scope->begin();
-    }
+  if (!needsLocalScopeForVarDecl(VD))
+    return Scope;
+  Scope = createOrReuseLocalScope(Scope);
+  Scope->addVar(VD);
+  ScopePos = Scope->begin();
   return Scope;
 }
 
@@ -3614,6 +3662,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;
+    }
   }
 }
 
@@ -3673,7 +3737,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) {
@@ -3700,7 +3765,7 @@
     if (Context.needsTempDtorBranch()) {
       Context.setDecisionPoint(Succ, E);
     }
-    appendTemporaryDtor(Block, E);
+    appendTemporaryDtor(Block, E, BindsParameter);
 
     B = Block;
   }
Index: lib/Analysis/LiveVariables.cpp
===================================================================
--- lib/Analysis/LiveVariables.cpp
+++ lib/Analysis/LiveVariables.cpp
@@ -462,6 +462,16 @@
       val.liveDecls = DSetFact.add(val.liveDecls, Dtor->getVarDecl());
       continue;
     }
+    if (Optional<CFGTemporaryDtor> Dtor =
+            elem.getAs<CFGTemporaryDtor>()) {
+      //llvm::errs() << "keep it live...\n";
+      //Dtor->getBindTemporaryExpr()->dump();
+//      llvm::errs() << "\n";
+      // 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/CoreEngine.cpp
===================================================================
--- lib/StaticAnalyzer/Core/CoreEngine.cpp
+++ lib/StaticAnalyzer/Core/CoreEngine.cpp
@@ -228,6 +228,8 @@
 
 void CoreEngine::dispatchWorkItem(ExplodedNode* Pred, ProgramPoint Loc,
                                   const WorkListUnit& WU) {
+  //Pred->getState()->dump();
+  //llvm::errs() << "\n";
   // Dispatch on the location type.
   switch (Loc.getKind()) {
     case ProgramPoint::BlockEdgeKind:
Index: lib/StaticAnalyzer/Core/ExprEngine.cpp
===================================================================
--- lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -191,6 +191,9 @@
       return State;
     Result = Ex;
   } else {
+   // if (const MemRegion *MR = V.getAsRegion())
+   //   if (isa<CXXTempObjectRegion>(MR->getBaseRegion()))
+   //     return State;
     // We need to create a region no matter what. For sanity, make sure we don't
     // try to stuff a Loc into a non-pointer temporary region.
     assert(!V.getAs<Loc>() || Loc::isLocType(Result->getType()) ||
@@ -422,6 +425,8 @@
 
 void ExprEngine::ProcessStmt(const CFGStmt S,
                              ExplodedNode *Pred) {
+  //S.getStmt()->dump();
+  //llvm::errs() << "\n";
   // Reclaim any unnecessary nodes in the ExplodedGraph.
   G.reclaimRecentlyAllocatedNodes();
 
@@ -686,9 +691,25 @@
   assert(CleanDtorState.size() <= 1);
   ExplodedNode *CleanPred =
       CleanDtorState.empty() ? Pred : *CleanDtorState.begin();
+
+  const LocationContext *LCtx = CleanPred->getLocationContext();
+  SVal Val = CleanPred->getState()->getSVal(
+      D.getBindTemporaryExpr()->getSubExpr(), LCtx->getCurrentStackFrame());
+  const MemRegion *Region = Val.getAsRegion();
+  //llvm::errs() << "huh?\n";
+  //if (Region != nullptr) {
+  //  llvm::errs() << "HEEEEEEEEEEEEEEEY!!!!!\n";
+ // }
+  // If the class does not have any members, there will not be a region
+  // for it bound in the environment.
+  //if (Optional<loc::MemRegionVal> MRV =
+  //        Val.getAs<loc::MemRegionVal>()) {
+  //  Region = LCV->getRegion();
+  //}
+
   // 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(),
+  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
@@ -204,7 +204,17 @@
   if (!MR)
     return false;
 
-  return isa<CXXTempObjectRegion>(MR);
+  return false;
+  /*(
+  if (isa<CXXTempObjectRegion>(MR) && !isa<CXXTemporaryObjectExpr>(E)) {
+    if (const CXXConstructExpr* CE = llvm::dyn_cast<CXXConstructExpr>(E)) {
+        return true;
+      E->dump();
+      llvm::errs() << "\n";
+    }
+  }
+  return false;
+*/
 }
 
 /// The call exit is simulated with a sequence of nodes, which occur between 
@@ -637,12 +647,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 +811,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/RegionStore.cpp
===================================================================
--- lib/StaticAnalyzer/Core/RegionStore.cpp
+++ lib/StaticAnalyzer/Core/RegionStore.cpp
@@ -1900,8 +1900,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())
@@ -2111,8 +2112,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/auto-obj-dtors-cfg-output.cpp
===================================================================
--- test/Analysis/auto-obj-dtors-cfg-output.cpp
+++ test/Analysis/auto-obj-dtors-cfg-output.cpp
@@ -42,7 +42,7 @@
 // CHECK-NEXT:   8: [B1.7] (ImplicitCastExpr, NoOp, const class A)
 // CHECK-NEXT:   9: [B1.8]
 // CHECK:       10: const A &c = A();
-// CHECK:       11: [B1.10].~A() (Implicit destructor)
+// CHECK:       11: ~A()
 // CHECK:       12: [B1.2].~A() (Implicit destructor)
 // CHECK-NEXT:   Preds (1): B2
 // CHECK-NEXT:   Succs (1): B0
Index: test/Analysis/cfg.cpp
===================================================================
--- test/Analysis/cfg.cpp
+++ test/Analysis/cfg.cpp
@@ -1,6 +1,8 @@
 // RUN: %clang_cc1 -analyze -analyzer-checker=debug.DumpCFG -triple x86_64-apple-darwin12 -analyzer-config cfg-temporary-dtors=true -std=c++11 %s > %t 2>&1
 // RUN: FileCheck --input-file=%t %s
 
+#include "Inputs/system-header-simulator-cxx.h"
+
 // CHECK-LABEL: void checkWrap(int i)
 // CHECK: ENTRY
 // CHECK-NEXT: Succs (1): B1
@@ -376,10 +378,9 @@
   MyClass* obj = new (buffer) MyClass[5];
 }
 
-
 // CHECK-LABEL: void test_lifetime_extended_temporaries()
 // CHECK: [B1]
-struct LifetimeExtend { LifetimeExtend(int); ~LifetimeExtend(); };
+struct LifetimeExtend { LifetimeExtend(int); ~LifetimeExtend(); int i; };
 struct Aggregate { const LifetimeExtend a; const LifetimeExtend b; };
 struct AggregateRef { const LifetimeExtend &a; const LifetimeExtend &b; };
 void test_lifetime_extended_temporaries() {
@@ -420,15 +421,36 @@
   }
   // CHECK: LifetimeExtend(5)
   // CHECK-NEXT: : 5
-  // FIXME: We want to emit the destructors of the lifetime
-  // extended variables here.
+  // CHECK-NEXT: ~LifetimeExtend()
+  // CHECK-NEXT: ~LifetimeExtend()
   // CHECK-NOT: ~LifetimeExtend()
   {
     AggregateRef a{LifetimeExtend(5), LifetimeExtend(5)};
     5;
   }
-  // FIXME: Add tests for lifetime extension via subobject
-  // references (LifetimeExtend().some_member).
+  // CHECK: LifetimeExtend(6)
+  // CHECK-NEXT: : 6
+  // CHECK-NEXT: ~LifetimeExtend()
+  // CHECK-NOT: ~LifetimeExtend()
+  {
+    const int &i = LifetimeExtend(6).i;
+    6;
+  }
+}
+
+
+// CHECK-LABEL: void test_lifetime_extended_temporaries_for_range()
+void test_lifetime_extended_temporaries_for_range() {
+  // CHECK: [B1]
+  // CHECK-NEXT: ~const LifetimeExtend [1]()
+  // CHECK-NEXT: Preds (1): B2
+  // CHECK: auto &&__range = { LifetimeExtend(7) };
+  // CHECK-NEXT: ~LifetimeExtend() (Temporary object destructor)
+  {
+    for (LifetimeExtend e : {LifetimeExtend(7)}) {
+      1;
+    }
+  }
 }
 
 
Index: test/Analysis/temp-obj-dtors-cfg-output.cpp
===================================================================
--- test/Analysis/temp-obj-dtors-cfg-output.cpp
+++ test/Analysis/temp-obj-dtors-cfg-output.cpp
@@ -982,7 +982,7 @@
 // CHECK:    12: [B1.7]([B1.11])
 // CHECK:    13: ~A() (Temporary object destructor)
 // CHECK:    14: int b;
-// CHECK:    15: [B1.5].~A() (Implicit destructor)
+// CHECK:    15: ~A() (Temporary object destructor)
 // CHECK:     Preds (1): B2
 // CHECK:     Succs (1): B0
 // CHECK:   [B0 (EXIT)]
@@ -1026,7 +1026,7 @@
 // CHECK:    16: [B1.9]([B1.15])
 // CHECK:    17: ~A() (Temporary object destructor)
 // CHECK:    18: int b;
-// CHECK:    19: [B1.7].~A() (Implicit destructor)
+// CHECK:    19: ~A() (Temporary object destructor)
 // CHECK:     Preds (1): B2
 // CHECK:     Succs (1): B0
 // CHECK:   [B0 (EXIT)]
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