Fix some cases of incorrect handling of lifetime extended temporaries.

MaterializeTemporaryExpr already contains information about the lifetime
of the temporary; if the lifetime is not the full statement, we do not
want to emit a destructor at the end of the full statement for it.

llvm-svn: 214292
This commit is contained in:
Manuel Klimek 2014-07-30 08:34:42 +00:00
parent 3b6c466e96
commit b0042c414e
2 changed files with 79 additions and 12 deletions

View File

@ -1000,21 +1000,17 @@ CFGBlock *CFGBuilder::addInitializer(CXXCtorInitializer *I) {
if (!BuildOpts.AddInitializers)
return Block;
bool IsReference = false;
bool HasTemporaries = false;
// Destructors of temporaries in initialization expression should be called
// after initialization finishes.
Expr *Init = I->getInit();
if (Init) {
if (FieldDecl *FD = I->getAnyMember())
IsReference = FD->getType()->isReferenceType();
HasTemporaries = isa<ExprWithCleanups>(Init);
if (BuildOpts.AddTemporaryDtors && HasTemporaries) {
// Generate destructors for temporaries in initialization expression.
VisitForTemporaryDtors(cast<ExprWithCleanups>(Init)->getSubExpr(),
IsReference);
VisitForTemporaryDtors(cast<ExprWithCleanups>(Init)->getSubExpr());
}
}
@ -1946,7 +1942,6 @@ CFGBlock *CFGBuilder::VisitDeclSubExpr(DeclStmt *DS) {
return Block;
}
bool IsReference = false;
bool HasTemporaries = false;
// Guard static initializers under a branch.
@ -1968,13 +1963,11 @@ CFGBlock *CFGBuilder::VisitDeclSubExpr(DeclStmt *DS) {
// after initialization finishes.
Expr *Init = VD->getInit();
if (Init) {
IsReference = VD->getType()->isReferenceType();
HasTemporaries = isa<ExprWithCleanups>(Init);
if (BuildOpts.AddTemporaryDtors && HasTemporaries) {
// Generate destructors for temporaries in initialization expression.
VisitForTemporaryDtors(cast<ExprWithCleanups>(Init)->getSubExpr(),
IsReference);
VisitForTemporaryDtors(cast<ExprWithCleanups>(Init)->getSubExpr());
}
}
@ -3492,13 +3485,32 @@ tryAgain:
E = cast<CastExpr>(E)->getSubExpr();
goto tryAgain;
case Stmt::CXXFunctionalCastExprClass:
// For functional cast we want BindToTemporary to be passed further.
E = cast<CXXFunctionalCastExpr>(E)->getSubExpr();
goto tryAgain;
case Stmt::ParenExprClass:
E = cast<ParenExpr>(E)->getSubExpr();
goto tryAgain;
case Stmt::MaterializeTemporaryExprClass:
E = cast<MaterializeTemporaryExpr>(E)->GetTemporaryExpr();
case Stmt::MaterializeTemporaryExprClass: {
const MaterializeTemporaryExpr* MTE = cast<MaterializeTemporaryExpr>(E);
BindToTemporary = (MTE->getStorageDuration() != SD_FullExpression);
SmallVector<const Expr *, 2> CommaLHSs;
SmallVector<SubobjectAdjustment, 2> Adjustments;
// Find the expression whose lifetime needs to be extended.
E = const_cast<Expr *>(
cast<MaterializeTemporaryExpr>(E)
->GetTemporaryExpr()
->skipRValueSubobjectAdjustments(CommaLHSs, Adjustments));
// Visit the skipped comma operator left-hand sides for other temporaries.
for (const Expr *CommaLHS : CommaLHSs) {
VisitForTemporaryDtors(const_cast<Expr *>(CommaLHS),
/*BindToTemporary=*/false);
}
goto tryAgain;
}
case Stmt::BlockExprClass:
// Don't recurse into blocks; their subexpressions don't get evaluated

View File

@ -1,4 +1,4 @@
// RUN: %clang_cc1 -analyze -analyzer-checker=debug.DumpCFG -triple x86_64-apple-darwin12 -std=c++11 %s > %t 2>&1
// 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
// CHECK-LABEL: void checkWrap(int i)
@ -377,6 +377,61 @@ void test_placement_new_array() {
}
// CHECK-LABEL: void test_lifetime_extended_temporaries()
// CHECK: [B1]
struct LifetimeExtend { LifetimeExtend(int); ~LifetimeExtend(); };
struct Aggregate { const LifetimeExtend a; const LifetimeExtend b; };
struct AggregateRef { const LifetimeExtend &a; const LifetimeExtend &b; };
void test_lifetime_extended_temporaries() {
// CHECK: LifetimeExtend(1);
// CHECK-NEXT: : 1
// CHECK-NEXT: ~LifetimeExtend()
// CHECK-NOT: ~LifetimeExtend()
{
const LifetimeExtend &l = LifetimeExtend(1);
1;
}
// CHECK: LifetimeExtend(2)
// CHECK-NEXT: ~LifetimeExtend()
// CHECK-NEXT: : 2
// CHECK-NOT: ~LifetimeExtend()
{
// No life-time extension.
const int &l = (LifetimeExtend(2), 2);
2;
}
// CHECK: LifetimeExtend(3)
// CHECK-NEXT: : 3
// CHECK-NEXT: ~LifetimeExtend()
// CHECK-NOT: ~LifetimeExtend()
{
// The last one is lifetime extended.
const LifetimeExtend &l = (3, LifetimeExtend(3));
3;
}
// CHECK: LifetimeExtend(4)
// CHECK-NEXT: ~LifetimeExtend()
// CHECK-NEXT: ~LifetimeExtend()
// CHECK-NEXT: : 4
// CHECK-NOT: ~LifetimeExtend()
{
Aggregate a{LifetimeExtend(4), LifetimeExtend(4)};
4;
}
// CHECK: LifetimeExtend(5)
// CHECK-NEXT: : 5
// FIXME: We want to emit the destructors of the lifetime
// extended variables here.
// CHECK-NOT: ~LifetimeExtend()
{
AggregateRef a{LifetimeExtend(5), LifetimeExtend(5)};
5;
}
// FIXME: Add tests for lifetime extension via subobject
// references (LifetimeExtend().some_member).
}
// CHECK-LABEL: int *PR18472()
// CHECK: [B2 (ENTRY)]
// CHECK-NEXT: Succs (1): B1