From: Manuel Klimek Date: Wed, 30 Jul 2014 08:34:42 +0000 (+0000) Subject: Fix some cases of incorrect handling of lifetime extended temporaries. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=00151dc7f17c51f2374980021f2935904d2b0fc7;p=clang 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. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@214292 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/Analysis/CFG.cpp b/lib/Analysis/CFG.cpp index 842a385fbc..b949c9ea59 100644 --- a/lib/Analysis/CFG.cpp +++ b/lib/Analysis/CFG.cpp @@ -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(Init); if (BuildOpts.AddTemporaryDtors && HasTemporaries) { // Generate destructors for temporaries in initialization expression. - VisitForTemporaryDtors(cast(Init)->getSubExpr(), - IsReference); + VisitForTemporaryDtors(cast(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(Init); if (BuildOpts.AddTemporaryDtors && HasTemporaries) { // Generate destructors for temporaries in initialization expression. - VisitForTemporaryDtors(cast(Init)->getSubExpr(), - IsReference); + VisitForTemporaryDtors(cast(Init)->getSubExpr()); } } @@ -3492,13 +3485,32 @@ tryAgain: E = cast(E)->getSubExpr(); goto tryAgain; + case Stmt::CXXFunctionalCastExprClass: + // For functional cast we want BindToTemporary to be passed further. + E = cast(E)->getSubExpr(); + goto tryAgain; + case Stmt::ParenExprClass: E = cast(E)->getSubExpr(); goto tryAgain; - case Stmt::MaterializeTemporaryExprClass: - E = cast(E)->GetTemporaryExpr(); + case Stmt::MaterializeTemporaryExprClass: { + const MaterializeTemporaryExpr* MTE = cast(E); + BindToTemporary = (MTE->getStorageDuration() != SD_FullExpression); + SmallVector CommaLHSs; + SmallVector Adjustments; + // Find the expression whose lifetime needs to be extended. + E = const_cast( + cast(E) + ->GetTemporaryExpr() + ->skipRValueSubobjectAdjustments(CommaLHSs, Adjustments)); + // Visit the skipped comma operator left-hand sides for other temporaries. + for (const Expr *CommaLHS : CommaLHSs) { + VisitForTemporaryDtors(const_cast(CommaLHS), + /*BindToTemporary=*/false); + } goto tryAgain; + } case Stmt::BlockExprClass: // Don't recurse into blocks; their subexpressions don't get evaluated diff --git a/test/Analysis/cfg.cpp b/test/Analysis/cfg.cpp index 65060b1687..28cc440c46 100644 --- a/test/Analysis/cfg.cpp +++ b/test/Analysis/cfg.cpp @@ -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