Dumping of intermediate state developed with Jordan...
http://reviews.llvm.org/D4740
Files:
include/clang/Analysis/CFG.h
include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h
lib/Analysis/CFG.cpp
lib/Analysis/LiveVariables.cpp
lib/StaticAnalyzer/Core/CallEvent.cpp
lib/StaticAnalyzer/Core/CoreEngine.cpp
lib/StaticAnalyzer/Core/ExprEngine.cpp
lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
lib/StaticAnalyzer/Core/RegionStore.cpp
test/Analysis/auto-obj-dtors-cfg-output.cpp
test/Analysis/cfg.cpp
test/Analysis/temp-obj-dtors-cfg-output.cpp
test/Analysis/temporaries.cpp
Index: include/clang/Analysis/CFG.h
===================================================================
--- include/clang/Analysis/CFG.h
+++ include/clang/Analysis/CFG.h
@@ -280,13 +280,15 @@
/// at the end of full expression for temporary object.
class CFGTemporaryDtor : public CFGImplicitDtor {
public:
- CFGTemporaryDtor(CXXBindTemporaryExpr *expr)
- : CFGImplicitDtor(TemporaryDtor, expr, nullptr) {}
+ CFGTemporaryDtor(CXXBindTemporaryExpr *expr, bool BindsParameter)
+ : CFGImplicitDtor(TemporaryDtor, expr, BindsParameter ? this : nullptr) {}
const CXXBindTemporaryExpr *getBindTemporaryExpr() const {
return static_cast<const CXXBindTemporaryExpr *>(Data1.getPointer());
}
+ bool bindsParameter() const { return Data2.getPointer(); }
+
private:
friend class CFGElement;
CFGTemporaryDtor() {}
@@ -676,8 +678,9 @@
Elements.push_back(CFGMemberDtor(FD), C);
}
- void appendTemporaryDtor(CXXBindTemporaryExpr *E, BumpVectorContext &C) {
- Elements.push_back(CFGTemporaryDtor(E), C);
+ void appendTemporaryDtor(CXXBindTemporaryExpr *E, BumpVectorContext &C,
+ bool BindsParameter) {
+ Elements.push_back(CFGTemporaryDtor(E, BindsParameter), C);
}
void appendAutomaticObjDtor(VarDecl *VD, Stmt *S, BumpVectorContext &C) {
Index: include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h
===================================================================
--- include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h
+++ include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h
@@ -290,7 +290,8 @@
static inline bool isLocType(QualType T) {
return T->isAnyPointerType() || T->isBlockPointerType() ||
- T->isReferenceType() || T->isNullPtrType();
+ T->isReferenceType() || T->isNullPtrType() ||
+ T->isRecordType();
}
private:
Index: lib/Analysis/CFG.cpp
===================================================================
--- lib/Analysis/CFG.cpp
+++ lib/Analysis/CFG.cpp
@@ -484,7 +484,8 @@
CFGBlock *VisitBinaryOperatorForTemporaryDtors(BinaryOperator *E,
TempDtorContext &Context);
CFGBlock *VisitCXXBindTemporaryExprForTemporaryDtors(
- CXXBindTemporaryExpr *E, bool BindToTemporary, TempDtorContext &Context);
+ CXXBindTemporaryExpr *E, bool BindToTemporary, TempDtorContext &Context,
+ bool BindsParameter = false);
CFGBlock *VisitConditionalOperatorForTemporaryDtors(
AbstractConditionalOperator *E, bool BindToTemporary,
TempDtorContext &Context);
@@ -505,6 +506,9 @@
return Visit(S, AddStmtChoice::AlwaysAdd);
}
CFGBlock *addInitializer(CXXCtorInitializer *I);
+ void addAutomaticObjDtorsForLifetimeExtendedTemporaries(
+ Stmt *E, bool LifetimeExtended = false);
+ void addAutomaticObjDtorsForVarDecl(VarDecl *VD, Stmt* S);
void addAutomaticObjDtors(LocalScope::const_iterator B,
LocalScope::const_iterator E, Stmt *S);
void addImplicitDtorsForDestructor(const CXXDestructorDecl *DD);
@@ -515,6 +519,9 @@
void addLocalScopeForStmt(Stmt *S);
LocalScope* addLocalScopeForDeclStmt(DeclStmt *DS,
LocalScope* Scope = nullptr);
+ bool needsLocalScopeForLifetimeExtendedTemporary(const Stmt* E);
+ bool needsLocalScopeForVarDecl(const VarDecl* VD);
+ bool needsLocalScopeForType(QualType QT);
LocalScope* addLocalScopeForVarDecl(VarDecl *VD, LocalScope* Scope = nullptr);
void addLocalScopeAndDtors(Stmt *S);
@@ -540,8 +547,9 @@
void appendMemberDtor(CFGBlock *B, FieldDecl *FD) {
B->appendMemberDtor(FD, cfg->getBumpVectorContext());
}
- void appendTemporaryDtor(CFGBlock *B, CXXBindTemporaryExpr *E) {
- B->appendTemporaryDtor(E, cfg->getBumpVectorContext());
+ void appendTemporaryDtor(CFGBlock *B, CXXBindTemporaryExpr *E,
+ bool BindsParameter) {
+ B->appendTemporaryDtor(E, cfg->getBumpVectorContext(), BindsParameter);
}
void appendAutomaticObjDtor(CFGBlock *B, VarDecl *VD, Stmt *S) {
B->appendAutomaticObjDtor(VD, S, cfg->getBumpVectorContext());
@@ -1101,52 +1109,87 @@
return Block;
}
-/// \brief Retrieve the type of the temporary object whose lifetime was
-/// extended by a local reference with the given initializer.
-static QualType getReferenceInitTemporaryType(ASTContext &Context,
- const Expr *Init) {
- while (true) {
- // Skip parentheses.
- Init = Init->IgnoreParens();
-
- // Skip through cleanups.
- if (const ExprWithCleanups *EWC = dyn_cast<ExprWithCleanups>(Init)) {
- Init = EWC->getSubExpr();
- continue;
- }
-
- // Skip through the temporary-materialization expression.
- if (const MaterializeTemporaryExpr *MTE
- = dyn_cast<MaterializeTemporaryExpr>(Init)) {
- Init = MTE->GetTemporaryExpr();
- continue;
+void CFGBuilder::addAutomaticObjDtorsForLifetimeExtendedTemporaries(
+ Stmt *E, bool LifetimeExtended) {
+ switch (E->getStmtClass()) {
+ // FIXME: What do we want to do for conditional operators and binary
+ // conditional operators?
+ case Stmt::MaterializeTemporaryExprClass: {
+ const MaterializeTemporaryExpr * MTE = cast<MaterializeTemporaryExpr>(E);
+ if (MTE->getStorageDuration() != SD_FullExpression) {
+ SmallVector<const Expr *, 2> CommaLHSs;
+ SmallVector<SubobjectAdjustment, 2> Adjustments;
+ assert(MTE->GetTemporaryExpr());
+ const Expr *ExtendedE =
+ MTE->GetTemporaryExpr()->skipRValueSubobjectAdjustments(CommaLHSs,
+ Adjustments);
+ addAutomaticObjDtorsForLifetimeExtendedTemporaries(
+ const_cast<Expr*>(ExtendedE), /*LifetimeExtended=*/true);
}
-
- // Skip derived-to-base and no-op casts.
- if (const CastExpr *CE = dyn_cast<CastExpr>(Init)) {
- if ((CE->getCastKind() == CK_DerivedToBase ||
- CE->getCastKind() == CK_UncheckedDerivedToBase ||
- CE->getCastKind() == CK_NoOp) &&
- Init->getType()->isRecordType()) {
- Init = CE->getSubExpr();
- continue;
+ } break;
+ case Stmt::CXXBindTemporaryExprClass:
+ // If the temporary is not lifetime extended, we'll handle it in
+ // VisitForTemporaryDtors instead.
+ if (LifetimeExtended) {
+ const CXXDestructorDecl *Dtor =
+ cast<CXXBindTemporaryExpr>(E)->getTemporary()->getDestructor();
+ if (Dtor->isNoReturn()) {
+ Block = createNoReturnBlock();
+ } else {
+ autoCreateBlock();
}
+
+ appendTemporaryDtor(Block, cast<CXXBindTemporaryExpr>(E), false);
}
-
- // Skip member accesses into rvalues.
- if (const MemberExpr *ME = dyn_cast<MemberExpr>(Init)) {
- if (!ME->isArrow() && ME->getBase()->isRValue()) {
- Init = ME->getBase();
- continue;
- }
+ break;
+ case Stmt::ConditionalOperatorClass:
+ case Stmt::BinaryConditionalOperatorClass:
+ // FIXME: Implement. If both the true and false branch yield lifetime
+ // extended temporaries, we need to add a branch that triggers on which
+ // temporary constructor was executed.
+ break;
+ default:
+ LifetimeExtended = false;
+ // Fallthrough.
+ case Stmt::ParenExprClass:
+ case Stmt::CXXFunctionalCastExprClass:
+ case Stmt::ImplicitCastExprClass:
+ case Stmt::ExprWithCleanupsClass:
+ case Stmt::CXXDefaultArgExprClass:
+ case Stmt::CXXDefaultInitExprClass:
+ for (Stmt *Child : E->children()) {
+ if (Child)
+ addAutomaticObjDtorsForLifetimeExtendedTemporaries(Child,
+ LifetimeExtended);
}
-
+ break;
+ case Stmt::BlockExprClass:
+ case Stmt::LambdaExprClass:
break;
}
+}
+
+void CFGBuilder::addAutomaticObjDtorsForVarDecl(VarDecl *VD,
+ Stmt *S) {
+ if (!VD->getType()->isReferenceType() &&
+ needsLocalScopeForType(VD->getType())) {
+ QualType QT = VD->getType();
+ QT = Context->getBaseElementType(QT);
- return Init->getType();
+ const CXXDestructorDecl *Dtor = QT->getAsCXXRecordDecl()->getDestructor();
+ if (Dtor->isNoReturn())
+ Block = createNoReturnBlock();
+ else
+ autoCreateBlock();
+
+ appendAutomaticObjDtor(Block, VD, S);
+ }
+ if (const Expr *Init = VD->getInit()) {
+ addAutomaticObjDtorsForLifetimeExtendedTemporaries(
+ const_cast<Expr *>(Init));
+ }
}
-
+
/// addAutomaticObjDtors - Add to current block automatic objects destructors
/// for objects in range of local scope positions. Use S as trigger statement
/// for destructors.
@@ -1170,22 +1213,7 @@
for (SmallVectorImpl<VarDecl*>::reverse_iterator I = Decls.rbegin(),
E = Decls.rend();
I != E; ++I) {
- // If this destructor is marked as a no-return destructor, we need to
- // create a new block for the destructor which does not have as a successor
- // anything built thus far: control won't flow out of this block.
- QualType Ty = (*I)->getType();
- if (Ty->isReferenceType()) {
- Ty = getReferenceInitTemporaryType(*Context, (*I)->getInit());
- }
- Ty = Context->getBaseElementType(Ty);
-
- const CXXDestructorDecl *Dtor = Ty->getAsCXXRecordDecl()->getDestructor();
- if (Dtor->isNoReturn())
- Block = createNoReturnBlock();
- else
- autoCreateBlock();
-
- appendAutomaticObjDtor(Block, *I, S);
+ addAutomaticObjDtorsForVarDecl(*I, S);
}
}
@@ -1283,6 +1311,58 @@
return Scope;
}
+bool CFGBuilder::needsLocalScopeForType(QualType QT) {
+ // Check for constant size array. Set type to array element type.
+ while (const ConstantArrayType *AT = Context->getAsConstantArrayType(QT)) {
+ if (AT->getSize() == 0)
+ return false;
+ QT = AT->getElementType();
+ }
+
+ // Check if type is a C++ class with non-trivial destructor.
+ if (const CXXRecordDecl *CD = QT->getAsCXXRecordDecl())
+ return !CD->hasTrivialDestructor();
+ return false;
+}
+
+bool CFGBuilder::needsLocalScopeForLifetimeExtendedTemporary(const Stmt* E) {
+ // FIXME: Handle cases where the scope would be introduced by a nested
+ // lambda or block.
+ switch (E->getStmtClass()) {
+ case Stmt::MaterializeTemporaryExprClass: {
+ const MaterializeTemporaryExpr * MTE = cast<MaterializeTemporaryExpr>(E);
+ if (MTE->getStorageDuration() != SD_FullExpression) {
+ SmallVector<const Expr *, 2> CommaLHSs;
+ SmallVector<SubobjectAdjustment, 2> Adjustments;
+ assert(MTE->GetTemporaryExpr());
+ const Expr *ExtendedE =
+ MTE->GetTemporaryExpr()->skipRValueSubobjectAdjustments(CommaLHSs,
+ Adjustments);
+ return needsLocalScopeForType(ExtendedE->getType()) ||
+ needsLocalScopeForLifetimeExtendedTemporary(ExtendedE);
+ }
+ } break;
+ default:
+ for (const Stmt *Child : E->children()) {
+ if (Child && needsLocalScopeForLifetimeExtendedTemporary(Child))
+ return true;
+ }
+ break;
+ }
+ return false;
+}
+
+bool CFGBuilder::needsLocalScopeForVarDecl(const VarDecl* VD) {
+ if (!VD->getType()->isReferenceType() &&
+ needsLocalScopeForType(VD->getType())) {
+ return true;
+ }
+ if (const Expr *Init = VD->getInit()) {
+ return needsLocalScopeForLifetimeExtendedTemporary(Init);
+ }
+ return false;
+}
+
/// addLocalScopeForVarDecl - Add LocalScope for variable declaration. It will
/// create add scope for automatic objects and temporary objects bound to
/// const reference. Will reuse Scope if not NULL.
@@ -1300,43 +1380,11 @@
default: return Scope;
}
- // Check for const references bound to temporary. Set type to pointee.
- QualType QT = VD->getType();
- if (QT.getTypePtr()->isReferenceType()) {
- // Attempt to determine whether this declaration lifetime-extends a
- // temporary.
- //
- // FIXME: This is incorrect. Non-reference declarations can lifetime-extend
- // temporaries, and a single declaration can extend multiple temporaries.
- // We should look at the storage duration on each nested
- // MaterializeTemporaryExpr instead.
- const Expr *Init = VD->getInit();
- if (!Init)
- return Scope;
- if (const ExprWithCleanups *EWC = dyn_cast<ExprWithCleanups>(Init))
- Init = EWC->getSubExpr();
- if (!isa<MaterializeTemporaryExpr>(Init))
- return Scope;
-
- // Lifetime-extending a temporary.
- QT = getReferenceInitTemporaryType(*Context, Init);
- }
-
- // Check for constant size array. Set type to array element type.
- while (const ConstantArrayType *AT = Context->getAsConstantArrayType(QT)) {
- if (AT->getSize() == 0)
- return Scope;
- QT = AT->getElementType();
- }
-
- // Check if type is a C++ class with non-trivial destructor.
- if (const CXXRecordDecl *CD = QT->getAsCXXRecordDecl())
- if (!CD->hasTrivialDestructor()) {
- // Add the variable to scope
- Scope = createOrReuseLocalScope(Scope);
- Scope->addVar(VD);
- ScopePos = Scope->begin();
- }
+ if (!needsLocalScopeForVarDecl(VD))
+ return Scope;
+ Scope = createOrReuseLocalScope(Scope);
+ Scope->addVar(VD);
+ ScopePos = Scope->begin();
return Scope;
}
@@ -3614,6 +3662,22 @@
case Stmt::CXXDefaultInitExprClass:
E = cast<CXXDefaultInitExpr>(E)->getExpr();
goto tryAgain;
+
+ case Stmt::CallExprClass: {
+ CFGBlock *B = Block;
+ for (Expr *Arg : cast<CallExpr>(E)->arguments()) {
+ if (!Arg) continue;
+ if (CXXBindTemporaryExpr *BTE = dyn_cast<CXXBindTemporaryExpr>(Arg)) {
+ if (CFGBlock *R = VisitCXXBindTemporaryExprForTemporaryDtors(
+ BTE, false, Context, /*BindsParameter=*/true)) {
+ B = R;
+ }
+ } else if (CFGBlock *R = VisitForTemporaryDtors(Arg, false, Context)) {
+ B = R;
+ }
+ }
+ return B;
+ }
}
}
@@ -3673,7 +3737,8 @@
}
CFGBlock *CFGBuilder::VisitCXXBindTemporaryExprForTemporaryDtors(
- CXXBindTemporaryExpr *E, bool BindToTemporary, TempDtorContext &Context) {
+ CXXBindTemporaryExpr *E, bool BindToTemporary, TempDtorContext &Context,
+ bool BindsParameter) {
// First add destructors for temporaries in subexpression.
CFGBlock *B = VisitForTemporaryDtors(E->getSubExpr(), false, Context);
if (!BindToTemporary) {
@@ -3700,7 +3765,7 @@
if (Context.needsTempDtorBranch()) {
Context.setDecisionPoint(Succ, E);
}
- appendTemporaryDtor(Block, E);
+ appendTemporaryDtor(Block, E, BindsParameter);
B = Block;
}
Index: lib/Analysis/LiveVariables.cpp
===================================================================
--- lib/Analysis/LiveVariables.cpp
+++ lib/Analysis/LiveVariables.cpp
@@ -462,6 +462,16 @@
val.liveDecls = DSetFact.add(val.liveDecls, Dtor->getVarDecl());
continue;
}
+ if (Optional<CFGTemporaryDtor> Dtor =
+ elem.getAs<CFGTemporaryDtor>()) {
+ //llvm::errs() << "keep it live...\n";
+ //Dtor->getBindTemporaryExpr()->dump();
+// llvm::errs() << "\n";
+ // Temporary objects need to survive until the destructor is called.
+ val.liveStmts = SSetFact.add(val.liveStmts,
+ Dtor->getBindTemporaryExpr()->getSubExpr());
+ continue;
+ }
if (!elem.getAs<CFGStmt>())
continue;
Index: lib/StaticAnalyzer/Core/CallEvent.cpp
===================================================================
--- lib/StaticAnalyzer/Core/CallEvent.cpp
+++ lib/StaticAnalyzer/Core/CallEvent.cpp
@@ -959,7 +959,6 @@
CFGElement E = (*B)[CalleeCtx->getIndex()];
assert(E.getAs<CFGImplicitDtor>() &&
"All other CFG elements should have exprs");
- assert(!E.getAs<CFGTemporaryDtor>() && "We don't handle temporaries yet");
SValBuilder &SVB = State->getStateManager().getSValBuilder();
const CXXDestructorDecl *Dtor = cast<CXXDestructorDecl>(CalleeCtx->getDecl());
Index: lib/StaticAnalyzer/Core/CoreEngine.cpp
===================================================================
--- lib/StaticAnalyzer/Core/CoreEngine.cpp
+++ lib/StaticAnalyzer/Core/CoreEngine.cpp
@@ -228,6 +228,8 @@
void CoreEngine::dispatchWorkItem(ExplodedNode* Pred, ProgramPoint Loc,
const WorkListUnit& WU) {
+ //Pred->getState()->dump();
+ //llvm::errs() << "\n";
// Dispatch on the location type.
switch (Loc.getKind()) {
case ProgramPoint::BlockEdgeKind:
Index: lib/StaticAnalyzer/Core/ExprEngine.cpp
===================================================================
--- lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -191,6 +191,9 @@
return State;
Result = Ex;
} else {
+ // if (const MemRegion *MR = V.getAsRegion())
+ // if (isa<CXXTempObjectRegion>(MR->getBaseRegion()))
+ // return State;
// We need to create a region no matter what. For sanity, make sure we don't
// try to stuff a Loc into a non-pointer temporary region.
assert(!V.getAs<Loc>() || Loc::isLocType(Result->getType()) ||
@@ -422,6 +425,8 @@
void ExprEngine::ProcessStmt(const CFGStmt S,
ExplodedNode *Pred) {
+ //S.getStmt()->dump();
+ //llvm::errs() << "\n";
// Reclaim any unnecessary nodes in the ExplodedGraph.
G.reclaimRecentlyAllocatedNodes();
@@ -686,9 +691,25 @@
assert(CleanDtorState.size() <= 1);
ExplodedNode *CleanPred =
CleanDtorState.empty() ? Pred : *CleanDtorState.begin();
+
+ const LocationContext *LCtx = CleanPred->getLocationContext();
+ SVal Val = CleanPred->getState()->getSVal(
+ D.getBindTemporaryExpr()->getSubExpr(), LCtx->getCurrentStackFrame());
+ const MemRegion *Region = Val.getAsRegion();
+ //llvm::errs() << "huh?\n";
+ //if (Region != nullptr) {
+ // llvm::errs() << "HEEEEEEEEEEEEEEEY!!!!!\n";
+ // }
+ // If the class does not have any members, there will not be a region
+ // for it bound in the environment.
+ //if (Optional<loc::MemRegionVal> MRV =
+ // Val.getAs<loc::MemRegionVal>()) {
+ // Region = LCV->getRegion();
+ //}
+
// FIXME: Inlining of temporary destructors is not supported yet anyway, so
// we just put a NULL region for now. This will need to be changed later.
- VisitCXXDestructor(varType, nullptr, D.getBindTemporaryExpr(),
+ VisitCXXDestructor(varType, Region, D.getBindTemporaryExpr(),
/*IsBase=*/false, CleanPred, Dst);
}
Index: lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
===================================================================
--- lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
+++ lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
@@ -204,7 +204,17 @@
if (!MR)
return false;
- return isa<CXXTempObjectRegion>(MR);
+ return false;
+ /*(
+ if (isa<CXXTempObjectRegion>(MR) && !isa<CXXTemporaryObjectExpr>(E)) {
+ if (const CXXConstructExpr* CE = llvm::dyn_cast<CXXConstructExpr>(E)) {
+ return true;
+ E->dump();
+ llvm::errs() << "\n";
+ }
+ }
+ return false;
+*/
}
/// The call exit is simulated with a sequence of nodes, which occur between
@@ -637,12 +647,6 @@
if (!Opts.mayInlineCXXMemberFunction(CIMK_Destructors))
return CIP_DisallowedAlways;
- // FIXME: This is a hack. We don't handle temporary destructors
- // right now, so we shouldn't inline their constructors.
- if (CtorExpr->getConstructionKind() == CXXConstructExpr::CK_Complete)
- if (!Target || !isa<DeclRegion>(Target))
- return CIP_DisallowedOnce;
-
break;
}
case CE_CXXDestructor: {
@@ -807,12 +811,15 @@
AnalysisDeclContextManager &ADCMgr = AMgr.getAnalysisDeclContextManager();
AnalysisDeclContext *CalleeADC = ADCMgr.getContext(D);
- // Temporary object destructor processing is currently broken, so we never
- // inline them.
- // FIXME: Remove this once temp destructors are working.
if (isa<CXXDestructorCall>(Call)) {
- if ((*currBldrCtx->getBlock())[currStmtIdx].getAs<CFGTemporaryDtor>())
- return false;
+ if (Optional<CFGTemporaryDtor> Dtor =
+ (*currBldrCtx->getBlock())[currStmtIdx].getAs<CFGTemporaryDtor>()) {
+ // We must not inline temporary destructors for temporaries that are
+ // bound to parameters, as we currently don't support correctly
+ // invalidating our knowledge about them when they escape.
+ //if (Dtor->bindsParameter())
+ // return false;
+ }
}
// The auto-synthesized bodies are essential to inline as they are
Index: lib/StaticAnalyzer/Core/RegionStore.cpp
===================================================================
--- lib/StaticAnalyzer/Core/RegionStore.cpp
+++ lib/StaticAnalyzer/Core/RegionStore.cpp
@@ -1900,8 +1900,9 @@
QualType Ty = TR->getValueType();
if (Ty->isArrayType())
return bindArray(B, TR, V);
- if (Ty->isStructureOrClassType())
+ if (Ty->isStructureOrClassType()) {
return bindStruct(B, TR, V);
+ }
if (Ty->isVectorType())
return bindVector(B, TR, V);
if (Ty->isUnionType())
@@ -2111,8 +2112,9 @@
// Handle lazy compound values and symbolic values.
if (Optional<nonloc::LazyCompoundVal> LCV =
V.getAs<nonloc::LazyCompoundVal>()) {
- if (Optional<RegionBindingsRef> NewB = tryBindSmallStruct(B, R, RD, *LCV))
+ if (Optional<RegionBindingsRef> NewB = tryBindSmallStruct(B, R, RD, *LCV)) {
return *NewB;
+ }
return bindAggregate(B, R, V);
}
if (V.getAs<nonloc::SymbolVal>())
Index: test/Analysis/auto-obj-dtors-cfg-output.cpp
===================================================================
--- test/Analysis/auto-obj-dtors-cfg-output.cpp
+++ test/Analysis/auto-obj-dtors-cfg-output.cpp
@@ -42,7 +42,7 @@
// CHECK-NEXT: 8: [B1.7] (ImplicitCastExpr, NoOp, const class A)
// CHECK-NEXT: 9: [B1.8]
// CHECK: 10: const A &c = A();
-// CHECK: 11: [B1.10].~A() (Implicit destructor)
+// CHECK: 11: ~A()
// CHECK: 12: [B1.2].~A() (Implicit destructor)
// CHECK-NEXT: Preds (1): B2
// CHECK-NEXT: Succs (1): B0
Index: test/Analysis/cfg.cpp
===================================================================
--- test/Analysis/cfg.cpp
+++ test/Analysis/cfg.cpp
@@ -1,6 +1,8 @@
// RUN: %clang_cc1 -analyze -analyzer-checker=debug.DumpCFG -triple x86_64-apple-darwin12 -analyzer-config cfg-temporary-dtors=true -std=c++11 %s > %t 2>&1
// RUN: FileCheck --input-file=%t %s
+#include "Inputs/system-header-simulator-cxx.h"
+
// CHECK-LABEL: void checkWrap(int i)
// CHECK: ENTRY
// CHECK-NEXT: Succs (1): B1
@@ -376,10 +378,9 @@
MyClass* obj = new (buffer) MyClass[5];
}
-
// CHECK-LABEL: void test_lifetime_extended_temporaries()
// CHECK: [B1]
-struct LifetimeExtend { LifetimeExtend(int); ~LifetimeExtend(); };
+struct LifetimeExtend { LifetimeExtend(int); ~LifetimeExtend(); int i; };
struct Aggregate { const LifetimeExtend a; const LifetimeExtend b; };
struct AggregateRef { const LifetimeExtend &a; const LifetimeExtend &b; };
void test_lifetime_extended_temporaries() {
@@ -420,15 +421,36 @@
}
// CHECK: LifetimeExtend(5)
// CHECK-NEXT: : 5
- // FIXME: We want to emit the destructors of the lifetime
- // extended variables here.
+ // CHECK-NEXT: ~LifetimeExtend()
+ // CHECK-NEXT: ~LifetimeExtend()
// CHECK-NOT: ~LifetimeExtend()
{
AggregateRef a{LifetimeExtend(5), LifetimeExtend(5)};
5;
}
- // FIXME: Add tests for lifetime extension via subobject
- // references (LifetimeExtend().some_member).
+ // CHECK: LifetimeExtend(6)
+ // CHECK-NEXT: : 6
+ // CHECK-NEXT: ~LifetimeExtend()
+ // CHECK-NOT: ~LifetimeExtend()
+ {
+ const int &i = LifetimeExtend(6).i;
+ 6;
+ }
+}
+
+
+// CHECK-LABEL: void test_lifetime_extended_temporaries_for_range()
+void test_lifetime_extended_temporaries_for_range() {
+ // CHECK: [B1]
+ // CHECK-NEXT: ~const LifetimeExtend [1]()
+ // CHECK-NEXT: Preds (1): B2
+ // CHECK: auto &&__range = { LifetimeExtend(7) };
+ // CHECK-NEXT: ~LifetimeExtend() (Temporary object destructor)
+ {
+ for (LifetimeExtend e : {LifetimeExtend(7)}) {
+ 1;
+ }
+ }
}
Index: test/Analysis/temp-obj-dtors-cfg-output.cpp
===================================================================
--- test/Analysis/temp-obj-dtors-cfg-output.cpp
+++ test/Analysis/temp-obj-dtors-cfg-output.cpp
@@ -982,7 +982,7 @@
// CHECK: 12: [B1.7]([B1.11])
// CHECK: 13: ~A() (Temporary object destructor)
// CHECK: 14: int b;
-// CHECK: 15: [B1.5].~A() (Implicit destructor)
+// CHECK: 15: ~A() (Temporary object destructor)
// CHECK: Preds (1): B2
// CHECK: Succs (1): B0
// CHECK: [B0 (EXIT)]
@@ -1026,7 +1026,7 @@
// CHECK: 16: [B1.9]([B1.15])
// CHECK: 17: ~A() (Temporary object destructor)
// CHECK: 18: int b;
-// CHECK: 19: [B1.7].~A() (Implicit destructor)
+// CHECK: 19: ~A() (Temporary object destructor)
// CHECK: Preds (1): B2
// CHECK: Succs (1): B0
// CHECK: [B0 (EXIT)]
Index: test/Analysis/temporaries.cpp
===================================================================
--- test/Analysis/temporaries.cpp
+++ test/Analysis/temporaries.cpp
@@ -104,9 +104,7 @@
#if __cplusplus >= 201103L
clang_analyzer_eval(((HasCtor){1, 42}).y == 42); // expected-warning{{TRUE}}
- // FIXME: should be TRUE, but we don't inline the constructors of
- // temporaries because we can't model their destructors yet.
- clang_analyzer_eval(((HasCtorDtor){1, 42}).y == 42); // expected-warning{{UNKNOWN}}
+ clang_analyzer_eval(((HasCtorDtor){1, 42}).y == 42); // expected-warning{{TRUE}}
#endif
}
}
@@ -347,6 +345,49 @@
}
}
+ struct NoWarnDerefDtor {
+ NoWarnDerefDtor(int *p) : p(p) {}
+ ~NoWarnDerefDtor() { *p = 23; } // no warning
+ int *p;
+ };
+ void testDtorInlining() {
+ int x;
+ (NoWarnDerefDtor(&x));
+ clang_analyzer_eval(x == 23); // expected-warning{{TRUE}}
+ }
+ void use(NoWarnDerefDtor);
+ void testArgumentDtorInliningPreventedByValue() {
+ int x;
+ NoWarnDerefDtor p(nullptr);
+ use(p);
+ p.p = &x;
+ }
+
+ struct WarnDerefDtor {
+ WarnDerefDtor(int *p) : p(p) {}
+ ~WarnDerefDtor() {
+ *p = 23; // expected-warning{{Dereference of null pointer}}
+ }
+ int *p;
+ };
+ void useRef(WarnDerefDtor& p) {
+ p.p = nullptr;
+ }
+ void testArgumentDtorInliningWorksByReference() {
+ int x;
+ WarnDerefDtor p(&x);
+ useRef(p);
+ }
+ void useConstRef(const NoWarnDerefDtor& p) {
+ const_cast<NoWarnDerefDtor&>(p).p = nullptr;
+ }
+ void testArgumentDtorInliningWorksByConstReference() {
+ int x;
+ // FIXME: This should warn. We need to implement correct handling of
+ // temporaries bound to parameters first.
+ useConstRef(NoWarnDerefDtor(&x));
+ }
+
void testIfAtEndOfLoop() {
int y = 0;
while (true) {
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits