forked from OSchip/llvm-project
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:
parent
3b6c466e96
commit
b0042c414e
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue