This is totally awesome. Thank you for working on this. > On Aug 7, 2014, at 3:42 AM, Manuel Klimek <[email protected]> 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 > [email protected] > http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits
_______________________________________________ cfe-commits mailing list [email protected] http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits
