r215096 - Re-applying r214962.
Manuel Klimek
klimek at google.com
Thu Aug 7 03:42:17 PDT 2014
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(); };
More information about the cfe-commits
mailing list