r215096 - Re-applying r214962.

Ted Kremenek kremenek at apple.com
Thu Aug 7 10:08:26 PDT 2014


This is totally awesome.  Thank you for working on this.

> On Aug 7, 2014, at 3:42 AM, Manuel Klimek <klimek at google.com> wrote:
> 
> Author: klimek
> Date: Thu Aug  7 05:42:17 2014
> New Revision: 215096
> 
> URL: http://llvm.org/viewvc/llvm-project?rev=215096&view=rev
> Log:
> Re-applying r214962.
> 
> Changes to the original patch:
> - model the CFG for temporary destructors in conditional operators so that
>  the destructors of the true and false branch are always exclusive. This
>  is necessary because we must not have impossible paths for the path
>  based analysis to work.
> - add multiple regression tests with ternary operators
> 
> Original description:
> Fix modelling of non-lifetime-extended temporary destructors in the
> analyzer.
> 
> Changes to the CFG:
> When creating the CFG for temporary destructors, we create a structure
> that mirrors the branch structure of the conditionally executed
> temporary constructors in a full expression.
> The branches we create use a CXXBindTemporaryExpr as terminator which
> corresponds to the temporary constructor which must have been executed
> to enter the destruction branch.
> 
> 2. Changes to the Analyzer:
> When we visit a CXXBindTemporaryExpr we mark the CXXBindTemporaryExpr as
> executed in the state; when we reach a branch that contains the
> corresponding CXXBindTemporaryExpr as terminator, we branch out
> depending on whether the corresponding CXXBindTemporaryExpr was marked
> as executed.
> 
> Modified:
>    cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h
>    cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
>    cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h
>    cfe/trunk/lib/Analysis/CFG.cpp
>    cfe/trunk/lib/StaticAnalyzer/Core/CoreEngine.cpp
>    cfe/trunk/lib/StaticAnalyzer/Core/ExprEngine.cpp
>    cfe/trunk/test/Analysis/temp-obj-dtors-cfg-output.cpp
>    cfe/trunk/test/Analysis/temporaries.cpp
>    cfe/trunk/test/SemaCXX/return-noreturn.cpp
> 
> Modified: cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h?rev=215096&r1=215095&r2=215096&view=diff
> ==============================================================================
> --- cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h (original)
> +++ cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h Thu Aug  7 05:42:17 2014
> @@ -95,6 +95,8 @@ private:
> 
>   void HandleBranch(const Stmt *Cond, const Stmt *Term, const CFGBlock *B,
>                     ExplodedNode *Pred);
> +  void HandleCleanupTemporaryBranch(const CXXBindTemporaryExpr *BTE,
> +                                    const CFGBlock *B, ExplodedNode *Pred);
> 
>   /// Handle conditional logic for running static initializers.
>   void HandleStaticInit(const DeclStmt *DS, const CFGBlock *B,
> 
> 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=215096&r1=215095&r2=215096&view=diff
> ==============================================================================
> --- cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h (original)
> +++ cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h Thu Aug  7 05:42:17 2014
> @@ -227,6 +227,15 @@ public:
>                      const CFGBlock *DstT,
>                      const CFGBlock *DstF) override;
> 
> +  /// Called by CoreEngine.
> +  /// Used to generate successor nodes for temporary destructors depending
> +  /// on whether the corresponding constructor was visited.
> +  void processCleanupTemporaryBranch(const CXXBindTemporaryExpr *BTE,
> +                                     NodeBuilderContext &BldCtx,
> +                                     ExplodedNode *Pred, ExplodedNodeSet &Dst,
> +                                     const CFGBlock *DstT,
> +                                     const CFGBlock *DstF) override;
> +
>   /// Called by CoreEngine.  Used to processing branching behavior
>   /// at static initalizers.
>   void processStaticInitializer(const DeclStmt *DS,
> @@ -408,7 +417,11 @@ public:
>   void VisitIncrementDecrementOperator(const UnaryOperator* U,
>                                        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/include/clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h?rev=215096&r1=215095&r2=215096&view=diff
> ==============================================================================
> --- cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h (original)
> +++ cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h Thu Aug  7 05:42:17 2014
> @@ -72,6 +72,16 @@ public:
>                              const CFGBlock *DstT,
>                              const CFGBlock *DstF) = 0;
> 
> +  /// Called by CoreEngine.
> +  /// Used to generate successor nodes for temporary destructors depending
> +  /// on whether the corresponding constructor was visited.
> +  virtual void processCleanupTemporaryBranch(const CXXBindTemporaryExpr *BTE,
> +                                             NodeBuilderContext &BldCtx,
> +                                             ExplodedNode *Pred,
> +                                             ExplodedNodeSet &Dst,
> +                                             const CFGBlock *DstT,
> +                                             const CFGBlock *DstF) = 0;
> +
>   /// Called by CoreEngine.  Used to processing branching behavior
>   /// at static initalizers.
>   virtual void processStaticInitializer(const DeclStmt *DS,
> 
> Modified: cfe/trunk/lib/Analysis/CFG.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/CFG.cpp?rev=215096&r1=215095&r2=215096&view=diff
> ==============================================================================
> --- cfe/trunk/lib/Analysis/CFG.cpp (original)
> +++ cfe/trunk/lib/Analysis/CFG.cpp Thu Aug  7 05:42:17 2014
> @@ -300,7 +300,7 @@ class CFGBuilder {
>   CFGBlock *SwitchTerminatedBlock;
>   CFGBlock *DefaultCaseBlock;
>   CFGBlock *TryTerminatedBlock;
> -  
> +
>   // Current position in local scope.
>   LocalScope::const_iterator ScopePos;
> 
> @@ -410,16 +410,76 @@ private:
>   CFGBlock *VisitChildren(Stmt *S);
>   CFGBlock *VisitNoRecurse(Expr *E, AddStmtChoice asc);
> 
> +  /// When creating the CFG for temporary destructors, we want to mirror the
> +  /// branch structure of the corresponding constructor calls.
> +  /// Thus, while visiting a statement for temporary destructors, we keep a
> +  /// context to keep track of the following information:
> +  /// - whether a subexpression is executed unconditionally
> +  /// - if a subexpression is executed conditionally, the first
> +  ///   CXXBindTemporaryExpr we encounter in that subexpression (which
> +  ///   corresponds to the last temporary destructor we have to call for this
> +  ///   subexpression) and the CFG block at that point (which will become the
> +  ///   successor block when inserting the decision point).
> +  ///
> +  /// That way, we can build the branch structure for temporary destructors as
> +  /// follows:
> +  /// 1. If a subexpression is executed unconditionally, we add the temporary
> +  ///    destructor calls to the current block.
> +  /// 2. If a subexpression is executed conditionally, when we encounter a
> +  ///    CXXBindTemporaryExpr:
> +  ///    a) If it is the first temporary destructor call in the subexpression,
> +  ///       we remember the CXXBindTemporaryExpr and the current block in the
> +  ///       TempDtorContext; we start a new block, and insert the temporary
> +  ///       destructor call.
> +  ///    b) Otherwise, add the temporary destructor call to the current block.
> +  ///  3. When we finished visiting a conditionally executed subexpression,
> +  ///     and we found at least one temporary constructor during the visitation
> +  ///     (2.a has executed), we insert a decision block that uses the
> +  ///     CXXBindTemporaryExpr as terminator, and branches to the current block
> +  ///     if the CXXBindTemporaryExpr was marked executed, and otherwise
> +  ///     branches to the stored successor.
> +  struct TempDtorContext {
> +    TempDtorContext(bool IsConditional)
> +        : IsConditional(IsConditional),
> +          Succ(nullptr),
> +          TerminatorExpr(nullptr) {}
> +
> +    /// Returns whether we need to start a new branch for a temporary destructor
> +    /// call. This is the case when the the temporary destructor is
> +    /// conditionally executed, and it is the first one we encounter while
> +    /// visiting a subexpression - other temporary destructors at the same level
> +    /// will be added to the same block and are executed under the same
> +    /// condition.
> +    bool needsTempDtorBranch() const {
> +      return IsConditional && !TerminatorExpr;
> +    }
> +
> +    /// Remember the successor S of a temporary destructor decision branch for
> +    /// the corresponding CXXBindTemporaryExpr E.
> +    void setDecisionPoint(CFGBlock *S, CXXBindTemporaryExpr *E) {
> +      Succ = S;
> +      TerminatorExpr = E;
> +    }
> +
> +    const bool IsConditional;
> +    CFGBlock *Succ;
> +    CXXBindTemporaryExpr *TerminatorExpr;
> +  };
> +
>   // Visitors to walk an AST and generate destructors of temporaries in
>   // full expression.
> -  CFGBlock *VisitForTemporaryDtors(Stmt *E, bool BindToTemporary = false);
> -  CFGBlock *VisitChildrenForTemporaryDtors(Stmt *E);
> -  CFGBlock *VisitBinaryOperatorForTemporaryDtors(BinaryOperator *E);
> -  CFGBlock *VisitCXXBindTemporaryExprForTemporaryDtors(CXXBindTemporaryExpr *E,
> -      bool BindToTemporary);
> -  CFGBlock *
> -  VisitConditionalOperatorForTemporaryDtors(AbstractConditionalOperator *E,
> -                                            bool BindToTemporary);
> +  CFGBlock *VisitForTemporaryDtors(Stmt *E, bool BindToTemporary,
> +                                   TempDtorContext &Context);
> +  CFGBlock *VisitChildrenForTemporaryDtors(Stmt *E, TempDtorContext &Context);
> +  CFGBlock *VisitBinaryOperatorForTemporaryDtors(BinaryOperator *E,
> +                                                 TempDtorContext &Context);
> +  CFGBlock *VisitCXXBindTemporaryExprForTemporaryDtors(
> +      CXXBindTemporaryExpr *E, bool BindToTemporary, TempDtorContext &Context);
> +  CFGBlock *VisitConditionalOperatorForTemporaryDtors(
> +      AbstractConditionalOperator *E, bool BindToTemporary,
> +      TempDtorContext &Context);
> +  void InsertTempDtorDecisionBlock(const TempDtorContext &Context,
> +                                   CFGBlock *FalseSucc = nullptr);
> 
>   // NYS == Not Yet Supported
>   CFGBlock *NYS() {
> @@ -1010,7 +1070,9 @@ CFGBlock *CFGBuilder::addInitializer(CXX
> 
>     if (BuildOpts.AddTemporaryDtors && HasTemporaries) {
>       // Generate destructors for temporaries in initialization expression.
> -      VisitForTemporaryDtors(cast<ExprWithCleanups>(Init)->getSubExpr());
> +      TempDtorContext Context(/*IsConditional=*/false);
> +      VisitForTemporaryDtors(cast<ExprWithCleanups>(Init)->getSubExpr(),
> +                             /*BindToTemporary=*/false, Context);
>     }
>   }
> 
> @@ -1967,7 +2029,9 @@ CFGBlock *CFGBuilder::VisitDeclSubExpr(D
> 
>     if (BuildOpts.AddTemporaryDtors && HasTemporaries) {
>       // Generate destructors for temporaries in initialization expression.
> -      VisitForTemporaryDtors(cast<ExprWithCleanups>(Init)->getSubExpr());
> +      TempDtorContext Context(/*IsConditional=*/false);
> +      VisitForTemporaryDtors(cast<ExprWithCleanups>(Init)->getSubExpr(),
> +                             /*BindToTemporary=*/false, Context);
>     }
>   }
> 
> @@ -3347,7 +3411,8 @@ CFGBlock *CFGBuilder::VisitExprWithClean
>   if (BuildOpts.AddTemporaryDtors) {
>     // If adding implicit destructors visit the full expression for adding
>     // destructors of temporaries.
> -    VisitForTemporaryDtors(E->getSubExpr());
> +    TempDtorContext Context(/*IsConditional=*/false);
> +    VisitForTemporaryDtors(E->getSubExpr(), false, Context);
> 
>     // Full expression has to be added as CFGStmt so it will be sequenced
>     // before destructors of it's temporaries.
> @@ -3456,7 +3521,8 @@ CFGBlock *CFGBuilder::VisitIndirectGotoS
>   return addStmt(I->getTarget());
> }
> 
> -CFGBlock *CFGBuilder::VisitForTemporaryDtors(Stmt *E, bool BindToTemporary) {
> +CFGBlock *CFGBuilder::VisitForTemporaryDtors(Stmt *E, bool BindToTemporary,
> +                                             TempDtorContext &Context) {
>   assert(BuildOpts.AddImplicitDtors && BuildOpts.AddTemporaryDtors);
> 
> tryAgain:
> @@ -3466,19 +3532,20 @@ tryAgain:
>   }
>   switch (E->getStmtClass()) {
>     default:
> -      return VisitChildrenForTemporaryDtors(E);
> +      return VisitChildrenForTemporaryDtors(E, Context);
> 
>     case Stmt::BinaryOperatorClass:
> -      return VisitBinaryOperatorForTemporaryDtors(cast<BinaryOperator>(E));
> +      return VisitBinaryOperatorForTemporaryDtors(cast<BinaryOperator>(E),
> +                                                  Context);
> 
>     case Stmt::CXXBindTemporaryExprClass:
>       return VisitCXXBindTemporaryExprForTemporaryDtors(
> -          cast<CXXBindTemporaryExpr>(E), BindToTemporary);
> +          cast<CXXBindTemporaryExpr>(E), BindToTemporary, Context);
> 
>     case Stmt::BinaryConditionalOperatorClass:
>     case Stmt::ConditionalOperatorClass:
>       return VisitConditionalOperatorForTemporaryDtors(
> -          cast<AbstractConditionalOperator>(E), BindToTemporary);
> +          cast<AbstractConditionalOperator>(E), BindToTemporary, Context);
> 
>     case Stmt::ImplicitCastExprClass:
>       // For implicit cast we want BindToTemporary to be passed further.
> @@ -3507,7 +3574,7 @@ tryAgain:
>       // Visit the skipped comma operator left-hand sides for other temporaries.
>       for (const Expr *CommaLHS : CommaLHSs) {
>         VisitForTemporaryDtors(const_cast<Expr *>(CommaLHS),
> -                               /*BindToTemporary=*/false);
> +                               /*BindToTemporary=*/false, Context);
>       }
>       goto tryAgain;
>     }
> @@ -3523,7 +3590,8 @@ tryAgain:
>       auto *LE = cast<LambdaExpr>(E);
>       CFGBlock *B = Block;
>       for (Expr *Init : LE->capture_inits()) {
> -        if (CFGBlock *R = VisitForTemporaryDtors(Init))
> +        if (CFGBlock *R = VisitForTemporaryDtors(
> +                Init, /*BindToTemporary=*/false, Context))
>           B = R;
>       }
>       return B;
> @@ -3539,7 +3607,13 @@ tryAgain:
>   }
> }
> 
> -CFGBlock *CFGBuilder::VisitChildrenForTemporaryDtors(Stmt *E) {
> +CFGBlock *CFGBuilder::VisitChildrenForTemporaryDtors(Stmt *E,
> +                                                     TempDtorContext &Context) {
> +  if (isa<LambdaExpr>(E)) {
> +    // Do not visit the children of lambdas; they have their own CFGs.
> +    return Block;
> +  }
> +
>   // When visiting children for destructors we want to visit them in reverse
>   // order that they will appear in the CFG.  Because the CFG is built
>   // bottom-up, this means we visit them in their natural order, which
> @@ -3547,165 +3621,114 @@ CFGBlock *CFGBuilder::VisitChildrenForTe
>   CFGBlock *B = Block;
>   for (Stmt::child_range I = E->children(); I; ++I) {
>     if (Stmt *Child = *I)
> -      if (CFGBlock *R = VisitForTemporaryDtors(Child))
> +      if (CFGBlock *R = VisitForTemporaryDtors(Child, false, Context))
>         B = R;
>   }
>   return B;
> }
> 
> -CFGBlock *CFGBuilder::VisitBinaryOperatorForTemporaryDtors(BinaryOperator *E) {
> +CFGBlock *CFGBuilder::VisitBinaryOperatorForTemporaryDtors(
> +    BinaryOperator *E, TempDtorContext &Context) {
>   if (E->isLogicalOp()) {
> -    // Destructors for temporaries in LHS expression should be called after
> -    // those for RHS expression. Even if this will unnecessarily create a block,
> -    // this block will be used at least by the full expression.
> -    autoCreateBlock();
> -    CFGBlock *ConfluenceBlock = VisitForTemporaryDtors(E->getLHS());
> -    if (badCFG)
> -      return nullptr;
> -
> -    Succ = ConfluenceBlock;
> -    Block = nullptr;
> -    CFGBlock *RHSBlock = VisitForTemporaryDtors(E->getRHS());
> -
> -    if (RHSBlock) {
> -      if (badCFG)
> -        return nullptr;
> -
> -      // If RHS expression did produce destructors we need to connect created
> -      // blocks to CFG in same manner as for binary operator itself.
> -      CFGBlock *LHSBlock = createBlock(false);
> -      LHSBlock->setTerminator(CFGTerminator(E, true));
> -
> -      // For binary operator LHS block is before RHS in list of predecessors
> -      // of ConfluenceBlock.
> -      std::reverse(ConfluenceBlock->pred_begin(),
> -          ConfluenceBlock->pred_end());
> -
> -      // See if this is a known constant.
> -      TryResult KnownVal = tryEvaluateBool(E->getLHS());
> -      if (KnownVal.isKnown() && (E->getOpcode() == BO_LOr))
> -        KnownVal.negate();
> -
> -      // Link LHSBlock with RHSBlock exactly the same way as for binary operator
> -      // itself.
> -      if (E->getOpcode() == BO_LOr) {
> -        addSuccessor(LHSBlock, KnownVal.isTrue() ? nullptr : ConfluenceBlock);
> -        addSuccessor(LHSBlock, KnownVal.isFalse() ? nullptr : RHSBlock);
> -      } else {
> -        assert (E->getOpcode() == BO_LAnd);
> -        addSuccessor(LHSBlock, KnownVal.isFalse() ? nullptr : RHSBlock);
> -        addSuccessor(LHSBlock, KnownVal.isTrue() ? nullptr : ConfluenceBlock);
> -      }
> -
> -      Block = LHSBlock;
> -      return LHSBlock;
> -    }
> -
> -    Block = ConfluenceBlock;
> -    return ConfluenceBlock;
> +    VisitForTemporaryDtors(E->getLHS(), false, Context);
> +    // We do not know at CFG-construction time whether the right-hand-side was
> +    // executed, thus we add a branch node that depends on the temporary
> +    // constructor call.
> +    TempDtorContext RHSContext(/*IsConditional=*/true);
> +    VisitForTemporaryDtors(E->getRHS(), false, RHSContext);
> +    InsertTempDtorDecisionBlock(RHSContext);
> +    return Block;
>   }
> 
>   if (E->isAssignmentOp()) {
>     // For assignment operator (=) LHS expression is visited
>     // before RHS expression. For destructors visit them in reverse order.
> -    CFGBlock *RHSBlock = VisitForTemporaryDtors(E->getRHS());
> -    CFGBlock *LHSBlock = VisitForTemporaryDtors(E->getLHS());
> +    CFGBlock *RHSBlock = VisitForTemporaryDtors(E->getRHS(), false, Context);
> +    CFGBlock *LHSBlock = VisitForTemporaryDtors(E->getLHS(), false, Context);
>     return LHSBlock ? LHSBlock : RHSBlock;
>   }
> 
>   // For any other binary operator RHS expression is visited before
>   // LHS expression (order of children). For destructors visit them in reverse
>   // order.
> -  CFGBlock *LHSBlock = VisitForTemporaryDtors(E->getLHS());
> -  CFGBlock *RHSBlock = VisitForTemporaryDtors(E->getRHS());
> +  CFGBlock *LHSBlock = VisitForTemporaryDtors(E->getLHS(), false, Context);
> +  CFGBlock *RHSBlock = VisitForTemporaryDtors(E->getRHS(), false, Context);
>   return RHSBlock ? RHSBlock : LHSBlock;
> }
> 
> CFGBlock *CFGBuilder::VisitCXXBindTemporaryExprForTemporaryDtors(
> -    CXXBindTemporaryExpr *E, bool BindToTemporary) {
> +    CXXBindTemporaryExpr *E, bool BindToTemporary, TempDtorContext &Context) {
>   // First add destructors for temporaries in subexpression.
> -  CFGBlock *B = VisitForTemporaryDtors(E->getSubExpr());
> +  CFGBlock *B = VisitForTemporaryDtors(E->getSubExpr(), false, Context);
>   if (!BindToTemporary) {
>     // If lifetime of temporary is not prolonged (by assigning to constant
>     // reference) add destructor for it.
> 
> -    // If the 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.
>     const CXXDestructorDecl *Dtor = E->getTemporary()->getDestructor();
> +
>     if (Dtor->isNoReturn()) {
> -      Succ = B;
> +      // If the 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.
> +      if (B) Succ = B;
>       Block = createNoReturnBlock();
> +    } else if (Context.needsTempDtorBranch()) {
> +      // If we need to introduce a branch, we add a new block that we will hook
> +      // up to a decision block later.
> +      if (B) Succ = B;
> +      Block = createBlock();
>     } else {
>       autoCreateBlock();
>     }
> -
> +    if (Context.needsTempDtorBranch()) {
> +      Context.setDecisionPoint(Succ, E);
> +    }
>     appendTemporaryDtor(Block, E);
> +
>     B = Block;
>   }
>   return B;
> }
> 
> -CFGBlock *CFGBuilder::VisitConditionalOperatorForTemporaryDtors(
> -    AbstractConditionalOperator *E, bool BindToTemporary) {
> -  // First add destructors for condition expression.  Even if this will
> -  // unnecessarily create a block, this block will be used at least by the full
> -  // expression.
> -  autoCreateBlock();
> -  CFGBlock *ConfluenceBlock = VisitForTemporaryDtors(E->getCond());
> -  if (badCFG)
> -    return nullptr;
> -  if (BinaryConditionalOperator *BCO
> -        = dyn_cast<BinaryConditionalOperator>(E)) {
> -    ConfluenceBlock = VisitForTemporaryDtors(BCO->getCommon());
> -    if (badCFG)
> -      return nullptr;
> -  }
> -
> -  // Try to add block with destructors for LHS expression.
> -  CFGBlock *LHSBlock = nullptr;
> -  Succ = ConfluenceBlock;
> -  Block = nullptr;
> -  LHSBlock = VisitForTemporaryDtors(E->getTrueExpr(), BindToTemporary);
> -  if (badCFG)
> -    return nullptr;
> -
> -  // Try to add block with destructors for RHS expression;
> -  Succ = ConfluenceBlock;
> -  Block = nullptr;
> -  CFGBlock *RHSBlock = VisitForTemporaryDtors(E->getFalseExpr(),
> -                                              BindToTemporary);
> -  if (badCFG)
> -    return nullptr;
> -
> -  if (!RHSBlock && !LHSBlock) {
> -    // If neither LHS nor RHS expression had temporaries to destroy don't create
> -    // more blocks.
> -    Block = ConfluenceBlock;
> -    return Block;
> +void CFGBuilder::InsertTempDtorDecisionBlock(const TempDtorContext &Context,
> +                                             CFGBlock *FalseSucc) {
> +  if (!Context.TerminatorExpr) {
> +    // If no temporary was found, we do not need to insert a decision point.
> +    return;
>   }
> +  assert(Context.TerminatorExpr);
> +  CFGBlock *Decision = createBlock(false);
> +  Decision->setTerminator(CFGTerminator(Context.TerminatorExpr, true));
> +  addSuccessor(Decision, Block);
> +  addSuccessor(Decision, FalseSucc ? FalseSucc : Context.Succ);
> +  Block = Decision;
> +}
> 
> -  Block = createBlock(false);
> -  Block->setTerminator(CFGTerminator(E, true));
> -  assert(Block->getTerminator().isTemporaryDtorsBranch());
> -
> -  // See if this is a known constant.
> -  const TryResult &KnownVal = tryEvaluateBool(E->getCond());
> -
> -  if (LHSBlock) {
> -    addSuccessor(Block, LHSBlock, !KnownVal.isFalse());
> -  } else if (KnownVal.isFalse()) {
> -    addSuccessor(Block, nullptr);
> +CFGBlock *CFGBuilder::VisitConditionalOperatorForTemporaryDtors(
> +    AbstractConditionalOperator *E, bool BindToTemporary,
> +    TempDtorContext &Context) {
> +  VisitForTemporaryDtors(E->getCond(), false, Context);
> +  CFGBlock *ConditionBlock = Block;
> +  CFGBlock *ConditionSucc = Succ;
> +
> +  TempDtorContext TrueContext(/*IsConditional=*/true);
> +  VisitForTemporaryDtors(E->getTrueExpr(), BindToTemporary, TrueContext);
> +  CFGBlock *TrueBlock = Block;
> +
> +  Block = ConditionBlock;
> +  Succ = ConditionSucc;
> +  TempDtorContext FalseContext(/*IsConditional=*/true);
> +  VisitForTemporaryDtors(E->getFalseExpr(), BindToTemporary, FalseContext);
> +
> +  if (TrueContext.TerminatorExpr && FalseContext.TerminatorExpr) {
> +    InsertTempDtorDecisionBlock(FalseContext, TrueBlock);
> +  } else if (TrueContext.TerminatorExpr) {
> +    Block = TrueBlock;
> +    InsertTempDtorDecisionBlock(TrueContext);
>   } else {
> -    addSuccessor(Block, ConfluenceBlock);
> -    std::reverse(ConfluenceBlock->pred_begin(), ConfluenceBlock->pred_end());
> +    InsertTempDtorDecisionBlock(FalseContext);
>   }
> -
> -  if (!RHSBlock)
> -    RHSBlock = ConfluenceBlock;
> -
> -  addSuccessor(Block, RHSBlock, !KnownVal.isTrue());
> -
>   return Block;
> }
> 
> 
> Modified: cfe/trunk/lib/StaticAnalyzer/Core/CoreEngine.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/CoreEngine.cpp?rev=215096&r1=215095&r2=215096&view=diff
> ==============================================================================
> --- cfe/trunk/lib/StaticAnalyzer/Core/CoreEngine.cpp (original)
> +++ cfe/trunk/lib/StaticAnalyzer/Core/CoreEngine.cpp Thu Aug  7 05:42:17 2014
> @@ -14,6 +14,7 @@
> 
> #include "clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h"
> #include "clang/AST/Expr.h"
> +#include "clang/AST/ExprCXX.h"
> #include "clang/AST/StmtCXX.h"
> #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
> #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
> @@ -346,6 +347,11 @@ void CoreEngine::HandleBlockExit(const C
>       default:
>         llvm_unreachable("Analysis for this terminator not implemented.");
> 
> +      case Stmt::CXXBindTemporaryExprClass:
> +        HandleCleanupTemporaryBranch(
> +            cast<CXXBindTemporaryExpr>(B->getTerminator().getStmt()), B, Pred);
> +        return;
> +
>       // Model static initializers.
>       case Stmt::DeclStmtClass:
>         HandleStaticInit(cast<DeclStmt>(Term), B, Pred);
> @@ -461,6 +467,17 @@ void CoreEngine::HandleBranch(const Stmt
>   enqueue(Dst);
> }
> 
> +void CoreEngine::HandleCleanupTemporaryBranch(const CXXBindTemporaryExpr *BTE,
> +                                              const CFGBlock *B,
> +                                              ExplodedNode *Pred) {
> +  assert(B->succ_size() == 2);
> +  NodeBuilderContext Ctx(*this, B, Pred);
> +  ExplodedNodeSet Dst;
> +  SubEng.processCleanupTemporaryBranch(BTE, Ctx, Pred, Dst, *(B->succ_begin()),
> +                                       *(B->succ_begin() + 1));
> +  // Enqueue the new frontier onto the worklist.
> +  enqueue(Dst);
> +}
> 
> void CoreEngine::HandleStaticInit(const DeclStmt *DS, const CFGBlock *B,
>                                   ExplodedNode *Pred) {
> 
> Modified: cfe/trunk/lib/StaticAnalyzer/Core/ExprEngine.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/ExprEngine.cpp?rev=215096&r1=215095&r2=215096&view=diff
> ==============================================================================
> --- cfe/trunk/lib/StaticAnalyzer/Core/ExprEngine.cpp (original)
> +++ cfe/trunk/lib/StaticAnalyzer/Core/ExprEngine.cpp Thu Aug  7 05:42:17 2014
> @@ -51,6 +51,15 @@ STATISTIC(NumMaxBlockCountReachedInInlin
> STATISTIC(NumTimesRetriedWithoutInlining,
>             "The # of times we re-evaluated a call without inlining");
> 
> +typedef std::pair<const CXXBindTemporaryExpr *, const StackFrameContext *>
> +    CXXBindTemporaryContext;
> +
> +// Keeps track of whether CXXBindTemporaryExpr nodes have been evaluated.
> +// The StackFrameContext assures that nested calls due to inlined recursive
> +// functions do not interfere.
> +REGISTER_TRAIT_WITH_PROGRAMSTATE(InitializedTemporariesSet,
> +                                 llvm::ImmutableSet<CXXBindTemporaryContext>)
> +
> //===----------------------------------------------------------------------===//
> // Engine construction and deletion.
> //===----------------------------------------------------------------------===//
> @@ -659,13 +668,59 @@ void ExprEngine::ProcessMemberDtor(const
> void ExprEngine::ProcessTemporaryDtor(const CFGTemporaryDtor D,
>                                       ExplodedNode *Pred,
>                                       ExplodedNodeSet &Dst) {
> +  ExplodedNodeSet CleanDtorState;
> +  StmtNodeBuilder StmtBldr(Pred, CleanDtorState, *currBldrCtx);
> +  ProgramStateRef State = Pred->getState();
> +  assert(State->contains<InitializedTemporariesSet>(
> +      std::make_pair(D.getBindTemporaryExpr(), Pred->getStackFrame())));
> +  State = State->remove<InitializedTemporariesSet>(
> +      std::make_pair(D.getBindTemporaryExpr(), Pred->getStackFrame()));
> +  StmtBldr.generateNode(D.getBindTemporaryExpr(), Pred, State);
> 
>   QualType varType = D.getBindTemporaryExpr()->getSubExpr()->getType();
> -
> -  // 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.
> +  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(),
> -                     /*IsBase=*/ false, Pred, Dst);
> +                     /*IsBase=*/false, CleanPred, Dst);
> +}
> +
> +void ExprEngine::processCleanupTemporaryBranch(const CXXBindTemporaryExpr *BTE,
> +                                               NodeBuilderContext &BldCtx,
> +                                               ExplodedNode *Pred,
> +                                               ExplodedNodeSet &Dst,
> +                                               const CFGBlock *DstT,
> +                                               const CFGBlock *DstF) {
> +  BranchNodeBuilder TempDtorBuilder(Pred, Dst, BldCtx, DstT, DstF);
> +  if (Pred->getState()->contains<InitializedTemporariesSet>(
> +          std::make_pair(BTE, Pred->getStackFrame()))) {
> +    TempDtorBuilder.markInfeasible(false);
> +    TempDtorBuilder.generateNode(Pred->getState(), true, Pred);
> +  } else {
> +    TempDtorBuilder.markInfeasible(true);
> +    TempDtorBuilder.generateNode(Pred->getState(), false, Pred);
> +  }
> +}
> +
> +void ExprEngine::VisitCXXBindTemporaryExpr(const CXXBindTemporaryExpr *BTE,
> +                                           ExplodedNodeSet &PreVisit,
> +                                           ExplodedNodeSet &Dst) {
> +  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();
> +    assert(!State->contains<InitializedTemporariesSet>(
> +        std::make_pair(BTE, Node->getStackFrame())));
> +    State = State->add<InitializedTemporariesSet>(
> +        std::make_pair(BTE, Node->getStackFrame()));
> +    StmtBldr.generateNode(BTE, Node, State);
> +  }
> }
> 
> void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
> @@ -773,6 +828,17 @@ void ExprEngine::Visit(const Stmt *S, Ex
>       // Handled due to fully linearised CFG.
>       break;
> 
> +    case Stmt::CXXBindTemporaryExprClass: {
> +      Bldr.takeNodes(Pred);
> +      ExplodedNodeSet PreVisit;
> +      getCheckerManager().runCheckersForPreStmt(PreVisit, Pred, S, *this);
> +      ExplodedNodeSet Next;
> +      VisitCXXBindTemporaryExpr(cast<CXXBindTemporaryExpr>(S), PreVisit, Next);
> +      getCheckerManager().runCheckersForPostStmt(Dst, Next, S, *this);
> +      Bldr.addNodes(Dst);
> +      break;
> +    }
> +
>     // Cases not handled yet; but will handle some day.
>     case Stmt::DesignatedInitExprClass:
>     case Stmt::ExtVectorElementExprClass:
> @@ -810,7 +876,6 @@ void ExprEngine::Visit(const Stmt *S, Ex
>     case Stmt::SizeOfPackExprClass:
>     case Stmt::StringLiteralClass:
>     case Stmt::ObjCStringLiteralClass:
> -    case Stmt::CXXBindTemporaryExprClass:
>     case Stmt::CXXPseudoDestructorExprClass:
>     case Stmt::SubstNonTypeTemplateParmExprClass:
>     case Stmt::CXXNullPtrLiteralExprClass: {
> @@ -1405,11 +1470,8 @@ static const Stmt *ResolveCondition(cons
>   if (!BO || !BO->isLogicalOp())
>     return Condition;
> 
> -  // FIXME: This is a workaround until we handle temporary destructor branches
> -  // correctly; currently, temporary destructor branches lead to blocks that
> -  // only have a terminator (and no statements). These blocks violate the
> -  // invariant this function assumes.
> -  if (B->getTerminator().isTemporaryDtorsBranch()) return Condition;
> +  assert(!B->getTerminator().isTemporaryDtorsBranch() &&
> +         "Temporary destructor branches handled by processBindTemporary.");
> 
>   // For logical operations, we still have the case where some branches
>   // use the traditional "merge" approach and others sink the branch
> @@ -1438,6 +1500,8 @@ void ExprEngine::processBranch(const Stm
>                                ExplodedNodeSet &Dst,
>                                const CFGBlock *DstT,
>                                const CFGBlock *DstF) {
> +  assert((!Condition || !isa<CXXBindTemporaryExpr>(Condition)) &&
> +         "CXXBindTemporaryExprs are handled by processBindTemporary.");
>   const LocationContext *LCtx = Pred->getLocationContext();
>   PrettyStackTraceLocationContext StackCrashInfo(LCtx);
>   currBldrCtx = &BldCtx;
> @@ -1601,10 +1665,29 @@ void ExprEngine::processIndirectGoto(Ind
>     builder.generateNode(I, state);
> }
> 
> +#if 0
> +static bool stackFrameDoesNotContainInitializedTemporaries(ExplodedNode &Pred) {
> +  const StackFrameContext* Frame = Pred.getStackFrame();
> +  const llvm::ImmutableSet<CXXBindTemporaryContext> &Set =
> +      Pred.getState()->get<InitializedTemporariesSet>();
> +  return std::find_if(Set.begin(), Set.end(),
> +                      [&](const CXXBindTemporaryContext &Ctx) {
> +                        if (Ctx.second == Frame) {
> +                          Ctx.first->dump();
> +                          llvm::errs() << "\n";
> +                        }
> +           return Ctx.second == Frame;
> +         }) == Set.end();
> +}
> +#endif
> +
> /// ProcessEndPath - Called by CoreEngine.  Used to generate end-of-path
> ///  nodes when the control reaches the end of a function.
> void ExprEngine::processEndOfFunction(NodeBuilderContext& BC,
>                                       ExplodedNode *Pred) {
> +  // FIXME: Assert that stackFrameDoesNotContainInitializedTemporaries(*Pred)).
> +  // We currently cannot enable this assert, as lifetime extended temporaries
> +  // are not modelled correctly.
>   PrettyStackTraceLocationContext CrashInfo(Pred->getLocationContext());
>   StateMgr.EndPath(Pred->getState());
> 
> 
> Modified: cfe/trunk/test/Analysis/temp-obj-dtors-cfg-output.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/temp-obj-dtors-cfg-output.cpp?rev=215096&r1=215095&r2=215096&view=diff
> ==============================================================================
> --- cfe/trunk/test/Analysis/temp-obj-dtors-cfg-output.cpp (original)
> +++ cfe/trunk/test/Analysis/temp-obj-dtors-cfg-output.cpp Thu Aug  7 05:42:17 2014
> @@ -324,7 +324,7 @@ int testConsistencyNestedNormalReturn(bo
> // CHECK:   [B3]
> // CHECK:     1: [B5.8] && [B4.5]
> // CHECK:     2: [B5.3]([B3.1])
> -// CHECK:     T: (Temp Dtor) [B5.8] && ...
> +// CHECK:     T: (Temp Dtor) [B4.2]
> // CHECK:     Preds (2): B4 B5
> // CHECK:     Succs (2): B2 B1
> // CHECK:   [B4]
> @@ -354,7 +354,7 @@ int testConsistencyNestedNormalReturn(bo
> // CHECK:   [B7]
> // CHECK:     1: [B9.5] && [B8.5]
> // CHECK:     2: bool a = A() && B();
> -// CHECK:     T: (Temp Dtor) [B9.5] && ...
> +// CHECK:     T: (Temp Dtor) [B8.2]
> // CHECK:     Preds (2): B8 B9
> // CHECK:     Succs (2): B6 B5
> // CHECK:   [B8]
> @@ -390,9 +390,9 @@ int testConsistencyNestedNormalReturn(bo
> // CHECK:   [B3]
> // CHECK:     1: [B5.8] || [B4.5]
> // CHECK:     2: [B5.3]([B3.1])
> -// CHECK:     T: (Temp Dtor) [B5.8] || ...
> +// CHECK:     T: (Temp Dtor) [B4.2]
> // CHECK:     Preds (2): B4 B5
> -// CHECK:     Succs (2): B1 B2
> +// CHECK:     Succs (2): B2 B1
> // CHECK:   [B4]
> // CHECK:     1: B() (CXXConstructExpr, class B)
> // CHECK:     2: [B4.1] (BindTemporary)
> @@ -420,9 +420,9 @@ int testConsistencyNestedNormalReturn(bo
> // CHECK:   [B7]
> // CHECK:     1: [B9.5] || [B8.5]
> // CHECK:     2: bool a = A() || B();
> -// CHECK:     T: (Temp Dtor) [B9.5] || ...
> +// CHECK:     T: (Temp Dtor) [B8.2]
> // CHECK:     Preds (2): B8 B9
> -// CHECK:     Succs (2): B5 B6
> +// CHECK:     Succs (2): B6 B5
> // CHECK:   [B8]
> // CHECK:     1: B() (CXXConstructExpr, class B)
> // CHECK:     2: [B8.1] (BindTemporary)
> @@ -492,9 +492,9 @@ int testConsistencyNestedNormalReturn(bo
> // CHECK:     3: [B7.2]
> // CHECK:     4: [B7.3] (CXXConstructExpr, class A)
> // CHECK:     5: A a = B() ? A() : A(B());
> -// CHECK:     T: (Temp Dtor) [B10.5] ? ... : ...
> +// CHECK:     T: (Temp Dtor) [B9.2]
> // CHECK:     Preds (2): B8 B9
> -// CHECK:     Succs (2): B5 B6
> +// CHECK:     Succs (2): B6 B5
> // CHECK:   [B8]
> // CHECK:     1: A() (CXXConstructExpr, class A)
> // CHECK:     2: [B8.1] (BindTemporary)
> @@ -533,7 +533,6 @@ int testConsistencyNestedNormalReturn(bo
> // CHECK:     Succs (2): B8 B9
> // CHECK:   [B0 (EXIT)]
> // CHECK:     Preds (1): B1
> -// CHECK:  C() : b_(true)
> // CHECK:   [B2 (ENTRY)]
> // CHECK:     Succs (1): B1
> // CHECK:   [B1]
> @@ -543,12 +542,10 @@ int testConsistencyNestedNormalReturn(bo
> // CHECK:     Succs (1): B0
> // CHECK:   [B0 (EXIT)]
> // CHECK:     Preds (1): B1
> -// CHECK:  ~C()
> // CHECK:   [B1 (ENTRY)]
> // CHECK:     Succs (1): B0
> // CHECK:   [B0 (EXIT)]
> // CHECK:     Preds (1): B1
> -// CHECK:  operator bool()
> // CHECK:   [B2 (ENTRY)]
> // CHECK:     Succs (1): B1
> // CHECK:   [B1]
> @@ -560,7 +557,6 @@ int testConsistencyNestedNormalReturn(bo
> // CHECK:     Succs (1): B0
> // CHECK:   [B0 (EXIT)]
> // CHECK:     Preds (1): B1
> -// CHECK:  D() : b_(true)
> // CHECK:   [B2 (ENTRY)]
> // CHECK:     Succs (1): B1
> // CHECK:   [B1]
> @@ -570,7 +566,6 @@ int testConsistencyNestedNormalReturn(bo
> // CHECK:     Succs (1): B0
> // CHECK:   [B0 (EXIT)]
> // CHECK:     Preds (1): B1
> -// CHECK:  operator bool()
> // CHECK:   [B2 (ENTRY)]
> // CHECK:     Succs (1): B1
> // CHECK:   [B1]
> @@ -582,7 +577,6 @@ int testConsistencyNestedNormalReturn(bo
> // CHECK:     Succs (1): B0
> // CHECK:   [B0 (EXIT)]
> // CHECK:     Preds (1): B1
> -// CHECK:  int test_cond_unnamed_custom_destructor()
> // CHECK:   [B4 (ENTRY)]
> // CHECK:     Succs (1): B3
> // CHECK:   [B1]
> @@ -607,7 +601,6 @@ int testConsistencyNestedNormalReturn(bo
> // CHECK:     Succs (2): B2 B1
> // CHECK:   [B0 (EXIT)]
> // CHECK:     Preds (2): B1 B2
> -// CHECK:  int test_cond_named_custom_destructor()
> // CHECK:   [B5 (ENTRY)]
> // CHECK:     Succs (1): B4
> // CHECK:   [B1]
> @@ -642,7 +635,6 @@ int testConsistencyNestedNormalReturn(bo
> // CHECK:     Succs (2): B3 B2
> // CHECK:   [B0 (EXIT)]
> // CHECK:     Preds (3): B1 B2 B3
> -// CHECK:  int test_cond_unnamed_auto_destructor()
> // CHECK:   [B4 (ENTRY)]
> // CHECK:     Succs (1): B3
> // CHECK:   [B1]
> @@ -665,7 +657,6 @@ int testConsistencyNestedNormalReturn(bo
> // CHECK:     Succs (2): B2 B1
> // CHECK:   [B0 (EXIT)]
> // CHECK:     Preds (2): B1 B2
> -// CHECK:  int test_cond_named_auto_destructor()
> // CHECK:   [B4 (ENTRY)]
> // CHECK:     Succs (1): B3
> // CHECK:   [B1]
> @@ -718,9 +709,9 @@ int testConsistencyNestedNormalReturn(bo
> // CHECK:     2: [B4.1] (ImplicitCastExpr, NoOp, const class A)
> // CHECK:     3: [B4.2]
> // CHECK:     4: [B7.3]([B4.3])
> -// CHECK:     T: (Temp Dtor) [B7.8] ? ... : ...
> +// CHECK:     T: (Temp Dtor) [B6.2]
> // CHECK:     Preds (2): B5 B6
> -// CHECK:     Succs (2): B2 B3
> +// CHECK:     Succs (2): B3 B2
> // CHECK:   [B5]
> // CHECK:     1: A() (CXXConstructExpr, class A)
> // CHECK:     2: [B5.1] (BindTemporary)
> @@ -775,9 +766,9 @@ int testConsistencyNestedNormalReturn(bo
> // CHECK:     2: [B10.1] (ImplicitCastExpr, NoOp, const class A)
> // CHECK:     3: [B10.2]
> // CHECK:     4: const A &a = B() ? A() : A(B());
> -// CHECK:     T: (Temp Dtor) [B13.5] ? ... : ...
> +// CHECK:     T: (Temp Dtor) [B12.2]
> // CHECK:     Preds (2): B11 B12
> -// CHECK:     Succs (2): B8 B9
> +// CHECK:     Succs (2): B9 B8
> // CHECK:   [B11]
> // CHECK:     1: A() (CXXConstructExpr, class A)
> // CHECK:     2: [B11.1] (BindTemporary)
> @@ -819,9 +810,8 @@ int testConsistencyNestedNormalReturn(bo
> // CHECK:   [B8 (ENTRY)]
> // CHECK:     Succs (1): B7
> // CHECK:   [B1]
> -// CHECK:     1: ~A() (Temporary object destructor)
> -// CHECK:     2: int b;
> -// CHECK:     3: [B4.5].~A() (Implicit destructor)
> +// CHECK:     1: int b;
> +// CHECK:     2: [B4.5].~A() (Implicit destructor)
> // CHECK:     Preds (2): B2 B3
> // CHECK:     Succs (1): B0
> // CHECK:   [B2]
> @@ -839,9 +829,9 @@ int testConsistencyNestedNormalReturn(bo
> // CHECK:     3: [B4.2]
> // CHECK:     4: [B4.3] (CXXConstructExpr, class A)
> // CHECK:     5: A a = A() ?: A();
> -// CHECK:     T: (Temp Dtor) [B7.5] ? ... : ...
> +// CHECK:     T: (Temp Dtor) [B6.2]
> // CHECK:     Preds (2): B5 B6
> -// CHECK:     Succs (2): B2 B3
> +// CHECK:     Succs (2): B3 B2
> // CHECK:   [B5]
> // CHECK:     1: [B7.2] (ImplicitCastExpr, NoOp, const class A)
> // CHECK:     2: [B5.1]
> @@ -872,9 +862,8 @@ int testConsistencyNestedNormalReturn(bo
> // CHECK:   [B13 (ENTRY)]
> // CHECK:     Succs (1): B12
> // CHECK:   [B1]
> -// CHECK:     1: ~A() (Temporary object destructor)
> -// CHECK:     2: int b;
> -// CHECK:     3: [B9.4].~A() (Implicit destructor)
> +// CHECK:     1: int b;
> +// CHECK:     2: [B9.4].~A() (Implicit destructor)
> // CHECK:     Preds (2): B2 B3
> // CHECK:     Succs (1): B0
> // CHECK:   [B2]
> @@ -887,15 +876,15 @@ int testConsistencyNestedNormalReturn(bo
> // CHECK:     Preds (1): B4
> // CHECK:     Succs (1): B1
> // CHECK:   [B4]
> -// CHECK:     1: [B7.5] ?: [B6.6]
> +// CHECK:     1: [B7.4] ?: [B6.6]
> // CHECK:     2: [B4.1] (ImplicitCastExpr, NoOp, const class A)
> // CHECK:     3: [B4.2]
> -// CHECK:     4: [B7.3]([B4.3])
> -// CHECK:     T: (Temp Dtor) [B7.8] ? ... : ...
> +// CHECK:     4: [B7.2]([B4.3])
> +// CHECK:     T: (Temp Dtor) [B6.2]
> // CHECK:     Preds (2): B5 B6
> -// CHECK:     Succs (2): B2 B3
> +// CHECK:     Succs (2): B3 B2
> // CHECK:   [B5]
> -// CHECK:     1: [B7.5] (ImplicitCastExpr, NoOp, const class A)
> +// CHECK:     1: [B7.4] (ImplicitCastExpr, NoOp, const class A)
> // CHECK:     2: [B5.1]
> // CHECK:     3: [B5.2] (CXXConstructExpr, class A)
> // CHECK:     4: [B5.3] (BindTemporary)
> @@ -911,16 +900,15 @@ int testConsistencyNestedNormalReturn(bo
> // CHECK:     Preds (1): B7
> // CHECK:     Succs (1): B4
> // CHECK:   [B7]
> -// CHECK:     1: ~A() (Temporary object destructor)
> -// CHECK:     2: foo
> -// CHECK:     3: [B7.2] (ImplicitCastExpr, FunctionToPointerDecay, void (*)(const class A &))
> -// CHECK:     4: A() (CXXConstructExpr, class A)
> -// CHECK:     5: [B7.4] (BindTemporary)
> -// CHECK:     6: [B7.5].operator bool
> -// CHECK:     7: [B7.5]
> -// CHECK:     8: [B7.7] (ImplicitCastExpr, UserDefinedConversion, _Bool)
> -// CHECK:     T: [B7.8] ? ... : ...
> -// CHECK:     Preds (2): B9 B8
> +// CHECK:     1: foo
> +// CHECK:     2: [B7.1] (ImplicitCastExpr, FunctionToPointerDecay, void (*)(const class A &))
> +// CHECK:     3: A() (CXXConstructExpr, class A)
> +// CHECK:     4: [B7.3] (BindTemporary)
> +// CHECK:     5: [B7.4].operator bool
> +// CHECK:     6: [B7.4]
> +// CHECK:     7: [B7.6] (ImplicitCastExpr, UserDefinedConversion, _Bool)
> +// CHECK:     T: [B7.7] ? ... : ...
> +// CHECK:     Preds (2): B8 B9
> // CHECK:     Succs (2): B5 B6
> // CHECK:   [B8]
> // CHECK:     1: ~A() (Temporary object destructor)
> @@ -931,9 +919,9 @@ int testConsistencyNestedNormalReturn(bo
> // CHECK:     2: [B9.1] (ImplicitCastExpr, NoOp, const class A)
> // CHECK:     3: [B9.2]
> // CHECK:     4: const A &a = A() ?: A();
> -// CHECK:     T: (Temp Dtor) [B12.5] ? ... : ...
> +// CHECK:     T: (Temp Dtor) [B11.2]
> // CHECK:     Preds (2): B10 B11
> -// CHECK:     Succs (2): B7 B8
> +// CHECK:     Succs (2): B8 B7
> // CHECK:   [B10]
> // CHECK:     1: [B12.2] (ImplicitCastExpr, NoOp, const class A)
> // CHECK:     2: [B10.1]
> @@ -1089,6 +1077,7 @@ int testConsistencyNestedNormalReturn(bo
> // CHECK:     Succs (1): B2
> // CHECK:   [B1]
> // CHECK:     1: int b;
> +// CHECK:     Preds (1): B2(Unreachable)
> // CHECK:     Succs (1): B0
> // CHECK:   [B2 (NORETURN)]
> // CHECK:     1: int a;
> @@ -1105,6 +1094,7 @@ int testConsistencyNestedNormalReturn(bo
> // CHECK:     Succs (1): B2
> // CHECK:   [B1]
> // CHECK:     1: int b;
> +// CHECK:     Preds (1): B2(Unreachable)
> // CHECK:     Succs (1): B0
> // CHECK:   [B2 (NORETURN)]
> // CHECK:     1: int a;
> @@ -1117,7 +1107,6 @@ int testConsistencyNestedNormalReturn(bo
> // CHECK:     Succs (1): B0
> // CHECK:   [B0 (EXIT)]
> // CHECK:     Preds (2): B1 B2
> -// CHECK:  int testConsistencyNestedSimple(bool value)
> // CHECK:   [B9 (ENTRY)]
> // CHECK:     Succs (1): B8
> // CHECK:   [B1]
> @@ -1132,7 +1121,7 @@ int testConsistencyNestedNormalReturn(bo
> // CHECK:     Succs (1): B0
> // CHECK:   [B3]
> // CHECK:     T: if [B5.1]
> -// CHECK:     Preds (1): B5
> +// CHECK:     Preds (2): B4(Unreachable) B5
> // CHECK:     Succs (2): B2 B1
> // CHECK:   [B4 (NORETURN)]
> // CHECK:     1: ~NoReturn() (Temporary object destructor)
> @@ -1140,9 +1129,9 @@ int testConsistencyNestedNormalReturn(bo
> // CHECK:     Succs (1): B0
> // CHECK:   [B5]
> // CHECK:     1: [B7.3] || [B6.7]
> -// CHECK:     T: (Temp Dtor) [B7.3] || ...
> +// CHECK:     T: (Temp Dtor) [B6.4]
> // CHECK:     Preds (2): B6 B7
> -// CHECK:     Succs (2): B3 B4
> +// CHECK:     Succs (2): B4 B3
> // CHECK:   [B6]
> // CHECK:     1: check
> // CHECK:     2: [B6.1] (ImplicitCastExpr, FunctionToPointerDecay, _Bool (*)(const class NoReturn &))
> @@ -1168,7 +1157,6 @@ int testConsistencyNestedNormalReturn(bo
> // CHECK:     Succs (2): B7 B1
> // CHECK:   [B0 (EXIT)]
> // CHECK:     Preds (3): B1 B2 B4
> -// CHECK:  int testConsistencyNestedComplex(bool value)
> // CHECK:   [B10 (ENTRY)]
> // CHECK:     Succs (1): B9
> // CHECK:   [B1]
> @@ -1183,7 +1171,7 @@ int testConsistencyNestedNormalReturn(bo
> // CHECK:     Succs (1): B0
> // CHECK:   [B3]
> // CHECK:     T: if [B5.1]
> -// CHECK:     Preds (1): B5
> +// CHECK:     Preds (2): B4(Unreachable) B5
> // CHECK:     Succs (2): B2 B1
> // CHECK:   [B4 (NORETURN)]
> // CHECK:     1: ~NoReturn() (Temporary object destructor)
> @@ -1191,9 +1179,9 @@ int testConsistencyNestedNormalReturn(bo
> // CHECK:     Succs (1): B0
> // CHECK:   [B5]
> // CHECK:     1: [B8.3] || [B7.3] || [B6.7]
> -// CHECK:     T: (Temp Dtor) [B8.3] || [B7.3] || ...
> +// CHECK:     T: (Temp Dtor) [B6.4]
> // CHECK:     Preds (3): B6 B7 B8
> -// CHECK:     Succs (2): B3 B4
> +// CHECK:     Succs (2): B4 B3
> // CHECK:   [B6]
> // CHECK:     1: check
> // CHECK:     2: [B6.1] (ImplicitCastExpr, FunctionToPointerDecay, _Bool (*)(const class NoReturn &))
> @@ -1226,7 +1214,6 @@ int testConsistencyNestedNormalReturn(bo
> // CHECK:     Succs (2): B8 B1
> // CHECK:   [B0 (EXIT)]
> // CHECK:     Preds (3): B1 B2 B4
> -// CHECK:  int testConsistencyNestedNormalReturn(bool value)
> // CHECK:   [B10 (ENTRY)]
> // CHECK:     Succs (1): B9
> // CHECK:   [B1]
> @@ -1241,7 +1228,7 @@ int testConsistencyNestedNormalReturn(bo
> // CHECK:     Succs (1): B0
> // CHECK:   [B3]
> // CHECK:     T: if [B5.1]
> -// CHECK:     Preds (1): B5
> +// CHECK:     Preds (2): B4(Unreachable) B5
> // CHECK:     Succs (2): B2 B1
> // CHECK:   [B4 (NORETURN)]
> // CHECK:     1: ~NoReturn() (Temporary object destructor)
> @@ -1249,9 +1236,9 @@ int testConsistencyNestedNormalReturn(bo
> // CHECK:     Succs (1): B0
> // CHECK:   [B5]
> // CHECK:     1: [B8.3] || [B7.2] || [B6.7]
> -// CHECK:     T: (Temp Dtor) [B8.3] || [B7.2] || ...
> +// CHECK:     T: (Temp Dtor) [B6.4]
> // CHECK:     Preds (3): B6 B7 B8
> -// CHECK:     Succs (2): B3 B4
> +// CHECK:     Succs (2): B4 B3
> // CHECK:   [B6]
> // CHECK:     1: check
> // CHECK:     2: [B6.1] (ImplicitCastExpr, FunctionToPointerDecay, _Bool (*)(const class NoReturn &))
> 
> Modified: cfe/trunk/test/Analysis/temporaries.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/temporaries.cpp?rev=215096&r1=215095&r2=215096&view=diff
> ==============================================================================
> --- cfe/trunk/test/Analysis/temporaries.cpp (original)
> +++ cfe/trunk/test/Analysis/temporaries.cpp Thu Aug  7 05:42:17 2014
> @@ -1,8 +1,9 @@
> // RUN: %clang_cc1 -analyze -analyzer-checker=core,debug.ExprInspection -verify -w -std=c++03 %s
> // RUN: %clang_cc1 -analyze -analyzer-checker=core,debug.ExprInspection -verify -w -std=c++11 %s
> -// RUN: %clang_cc1 -analyze -analyzer-checker=core,debug.ExprInspection -DTEMPORARY_DTORS -verify -w -analyzer-config cfg-temporary-dtors=true %s 
> +// RUN: %clang_cc1 -analyze -analyzer-checker=core,debug.ExprInspection -DTEMPORARY_DTORS -verify -w -analyzer-config cfg-temporary-dtors=true %s -std=c++11
> 
> extern bool clang_analyzer_eval(bool);
> +extern bool clang_analyzer_warnIfReached();
> 
> struct Trivial {
>   Trivial(int x) : value(x) {}
> @@ -111,13 +112,13 @@ namespace compound_literals {
> }
> 
> namespace destructors {
> -  void testPR16664andPR18159Crash() {
> -    struct Dtor {
> -      ~Dtor();
> -    };
> -    extern bool coin();
> -    extern bool check(const Dtor &);
> +  struct Dtor {
> +    ~Dtor();
> +  };
> +  extern bool coin();
> +  extern bool check(const Dtor &);
> 
> +  void testPR16664andPR18159Crash() {
>     // Regression test: we used to assert here when tmp dtors are enabled.
>     // PR16664 and PR18159
>     if (coin() && (coin() || coin() || check(Dtor()))) {
> @@ -193,8 +194,7 @@ namespace destructors {
>                 (i == 4 || i == 4 ||
>                  compute(i == 5 && (i == 4 || check(NoReturnDtor()))))) ||
>         i != 4) {
> -      // FIXME: This shouldn't cause a warning.
> -      clang_analyzer_eval(true);  // expected-warning{{TRUE}}
> +      clang_analyzer_eval(true);  // no warning, unreachable code
>     }
>   }
> 
> @@ -211,8 +211,7 @@ namespace destructors {
>   void testConsistencyNestedComplex(bool value) {
>     if (value) {
>       if (!value || !value || check(NoReturnDtor())) {
> -        // FIXME: This shouldn't cause a warning.
> -        clang_analyzer_eval(true); // expected-warning{{TRUE}}
> +        clang_analyzer_eval(true);  // no warning, unreachable code
>       }
>     }
>   }
> @@ -225,6 +224,120 @@ namespace destructors {
>       }
>     }
>   }
> +  // PR16664 and PR18159
> +  void testConsistencyNestedComplexMidBranch(bool value) {
> +    if (value) {
> +      if (!value || !value || check(NoReturnDtor()) || value) {
> +        clang_analyzer_eval(true);  // no warning, unreachable code
> +      }
> +    }
> +  }
> +
> +  // PR16664 and PR18159
> +  void testConsistencyNestedComplexNestedBranch(bool value) {
> +    if (value) {
> +      if (!value || (!value || check(NoReturnDtor()) || value)) {
> +        clang_analyzer_eval(true);  // no warning, unreachable code
> +      }
> +    }
> +  }
> +
> +  // PR16664 and PR18159
> +  void testConsistencyNestedVariableModification(bool value) {
> +    bool other = true;
> +    if (value) {
> +      if (!other || !value || (other = false) || check(NoReturnDtor()) ||
> +          !other) {
> +        clang_analyzer_eval(true);  // no warning, unreachable code
> +      }
> +    }
> +  }
> +
> +  void testTernaryNoReturnTrueBranch(bool value) {
> +    if (value) {
> +      bool b = value && (value ? check(NoReturnDtor()) : true);
> +      clang_analyzer_eval(true);  // no warning, unreachable code
> +    }
> +  }
> +  void testTernaryNoReturnFalseBranch(bool value) {
> +    if (value) {
> +      bool b = !value && !value ? true : check(NoReturnDtor());
> +      clang_analyzer_eval(true);  // no warning, unreachable code
> +    }
> +  }
> +  void testTernaryIgnoreNoreturnBranch(bool value) {
> +    if (value) {
> +      bool b = !value && !value ? check(NoReturnDtor()) : true;
> +      clang_analyzer_eval(true);  // expected-warning{{TRUE}}
> +    }
> +  }
> +  void testTernaryTrueBranchReached(bool value) {
> +    value ? clang_analyzer_warnIfReached() : // expected-warning{{REACHABLE}}
> +            check(NoReturnDtor());
> +  }
> +  void testTernaryFalseBranchReached(bool value) {
> +    value ? check(NoReturnDtor()) :
> +            clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
> +  }
> +
> +  void testLoop() {
> +    for (int i = 0; i < 10; ++i) {
> +      if (i < 3 && (i >= 2 || check(NoReturnDtor()))) {
> +        clang_analyzer_eval(true);  // no warning, unreachable code
> +      }
> +    }
> +  }
> +
> +  bool testRecursiveFrames(bool isInner) {
> +    if (isInner ||
> +        (clang_analyzer_warnIfReached(), false) || // expected-warning{{REACHABLE}}
> +        check(NoReturnDtor()) ||
> +        testRecursiveFrames(true)) {
> +      clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
> +    }
> +  }
> +  void testRecursiveFramesStart() { testRecursiveFrames(false); }
> +
> +  void testLambdas() {
> +    // This is the test we would like to write:
> +    // []() { check(NoReturnDtor()); } != nullptr || check(Dtor());
> +    // But currently the analyzer stops when it encounters a lambda:
> +    [] {};
> +    // The CFG for this now looks correct, but we still do not reach the line
> +    // below.
> +    clang_analyzer_warnIfReached(); // FIXME: Should warn.
> +  }
> +
> +  void testGnuExpressionStatements(int v) {
> +    ({ ++v; v == 10 || check(NoReturnDtor()); v == 42; }) || v == 23;
> +    clang_analyzer_warnIfReached();  // expected-warning{{REACHABLE}}
> +
> +    ({ ++v; check(NoReturnDtor()); v == 42; }) || v == 23;
> +    clang_analyzer_warnIfReached();  // no warning, unreachable code
> +  }
> +
> +  void testGnuExpressionStatementsDestructionPoint(int v) {
> +    // In normal context, the temporary destructor runs at the end of the full
> +    // statement, thus the last statement is reached.
> +    (++v, check(NoReturnDtor()), v == 42),
> +        clang_analyzer_warnIfReached();  // expected-warning{{REACHABLE}}
> +
> +    // GNU expression statements execute temporary destructors within the
> +    // blocks, thus the last statement is not reached.
> +    ({ ++v; check(NoReturnDtor()); v == 42; }),
> +        clang_analyzer_warnIfReached();  // no warning, unreachable code
> +  }
> +
> +  void testMultipleTemporaries(bool value) {
> +    if (value) {
> +      // FIXME: Find a way to verify construction order.
> +      // ~Dtor should run before ~NoReturnDtor() because construction order is
> +      // guaranteed by comma operator.
> +      if (!value || check((NoReturnDtor(), Dtor())) || value) {
> +        clang_analyzer_eval(true);  // no warning, unreachable code
> +      }
> +    }
> +  }
> 
>   void testBinaryOperatorShortcut(bool value) {
>     if (value) {
> @@ -234,6 +347,52 @@ namespace destructors {
>     }
>   }
> 
> +  void testIfAtEndOfLoop() {
> +    int y = 0;
> +    while (true) {
> +      if (y > 0) {
> +        clang_analyzer_warnIfReached();  // expected-warning{{REACHABLE}}
> +      }
> +      ++y;
> +      // Test that the CFG gets hooked up correctly when temporary destructors
> +      // are handled after a statically known branch condition.
> +      if (true) (void)0; else (void)check(NoReturnDtor());
> +    }
> +  }
> +
> +  void testTernaryAtEndOfLoop() {
> +    int y = 0;
> +    while (true) {
> +      if (y > 0) {
> +        clang_analyzer_warnIfReached();  // expected-warning{{REACHABLE}}
> +      }
> +      ++y;
> +      // Test that the CFG gets hooked up correctly when temporary destructors
> +      // are handled after a statically known branch condition.
> +      true ? (void)0 : (void)check(NoReturnDtor());
> +    }
> +  }
> +
> +  void testNoReturnInComplexCondition() {
> +    check(Dtor()) &&
> +        (check(NoReturnDtor()) || check(NoReturnDtor())) && check(Dtor());
> +    clang_analyzer_warnIfReached();  // expected-warning{{REACHABLE}}
> +  }
> +
> +  void testSequencingOfConditionalTempDtors(bool b) {
> +    b || (check(Dtor()), check(NoReturnDtor()));
> +    clang_analyzer_warnIfReached();  // expected-warning{{REACHABLE}}
> +  }
> +
> +  void testSequencingOfConditionalTempDtors2(bool b) {
> +    (b || check(Dtor())), check(NoReturnDtor());
> +    clang_analyzer_warnIfReached();  // no warning, unreachable code
> +  }
> +
> +  void testSequencingOfConditionalTempDtorsWithinBinaryOperators(bool b) {
> +    b || (check(Dtor()) + check(NoReturnDtor()));
> +    clang_analyzer_warnIfReached();  // expected-warning{{REACHABLE}}
> +  }
> #endif // TEMPORARY_DTORS
> }
> 
> 
> Modified: cfe/trunk/test/SemaCXX/return-noreturn.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/return-noreturn.cpp?rev=215096&r1=215095&r2=215096&view=diff
> ==============================================================================
> --- cfe/trunk/test/SemaCXX/return-noreturn.cpp (original)
> +++ cfe/trunk/test/SemaCXX/return-noreturn.cpp Thu Aug  7 05:42:17 2014
> @@ -146,6 +146,28 @@ void PR9412_f() {
>     PR9412_t<PR9412_Exact>(); // expected-note {{in instantiation of function template specialization 'PR9412_t<0>' requested here}}
> }
> 
> +struct NoReturn {
> +  ~NoReturn() __attribute__((noreturn));
> +  operator bool() const;
> +};
> +struct Return {
> +  ~Return();
> +  operator bool() const;
> +};
> +
> +int testTernaryUnconditionalNoreturn() {
> +  true ? NoReturn() : NoReturn();
> +}
> +
> +int testTernaryConditionalNoreturnTrueBranch(bool value) {
> +  value ? (NoReturn() || NoReturn()) : Return();
> +} // expected-warning {{control may reach end of non-void function}}
> +
> +int testTernaryConditionalNoreturnFalseBranch(bool value) {
> +  value ? Return() : (NoReturn() || NoReturn());
> +} // expected-warning {{control may reach end of non-void function}}
> +
> +
> #if __cplusplus >= 201103L
> namespace LambdaVsTemporaryDtor {
>   struct Y { ~Y(); };
> 
> 
> _______________________________________________
> cfe-commits mailing list
> cfe-commits at cs.uiuc.edu
> http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits




More information about the cfe-commits mailing list