Adapt now working cfg tests.
Handle more cases for lifetime extension.
Fix bug in materialize-temporary handling.
http://reviews.llvm.org/D4706
Files:
lib/Analysis/CFG.cpp
test/Analysis/auto-obj-dtors-cfg-output.cpp
test/Analysis/cfg.cpp
test/Analysis/temp-obj-dtors-cfg-output.cpp
Index: lib/Analysis/CFG.cpp
===================================================================
--- lib/Analysis/CFG.cpp
+++ lib/Analysis/CFG.cpp
@@ -435,6 +435,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);
@@ -445,6 +448,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);
@@ -1029,52 +1035,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));
}
-
- // 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);
+
+ const CXXDestructorDecl *Dtor = QT->getAsCXXRecordDecl()->getDestructor();
+ if (Dtor->isNoReturn())
+ Block = createNoReturnBlock();
+ else
+ autoCreateBlock();
- return Init->getType();
+ 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.
@@ -1098,22 +1139,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);
}
}
@@ -1211,6 +1237,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.
@@ -1228,43 +1306,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;
}
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
@@ -698,7 +698,6 @@
// CHECK: [B1]
// CHECK: 1: ~B() (Temporary object destructor)
// CHECK: 2: int b;
-// CHECK: 3: [B10.4].~A() (Implicit destructor)
// CHECK: Preds (2): B2 B3
// CHECK: Succs (1): B0
// CHECK: [B2]
@@ -874,7 +873,6 @@
// CHECK: [B1]
// CHECK: 1: ~A() (Temporary object destructor)
// CHECK: 2: int b;
-// CHECK: 3: [B9.4].~A() (Implicit destructor)
// CHECK: Preds (2): B2 B3
// CHECK: Succs (1): B0
// CHECK: [B2]
@@ -994,7 +992,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)]
@@ -1038,7 +1036,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)]
@@ -1089,6 +1087,7 @@
// 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 +1104,7 @@
// 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;
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits