Index: lib/Analysis/CFG.cpp =================================================================== --- lib/Analysis/CFG.cpp +++ lib/Analysis/CFG.cpp @@ -1000,21 +1000,17 @@ 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 @@ return Block; } - bool IsReference = false; bool HasTemporaries = false; // Guard static initializers under a branch. @@ -1968,13 +1963,11 @@ // 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 @@ 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 Index: test/Analysis/cfg.cpp =================================================================== --- test/Analysis/cfg.cpp +++ 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,42 @@ } +// CHECK-LABEL: void test_lifetime_extended_temporaries() +// CHECK: [B1] +// CHECK: LifetimeExtend(1); +// CHECK-NEXT: : 1 +// CHECK-NEXT: ~LifetimeExtend() +// CHECK-NOT: ~LifetimeExtend() +// CHECK: LifetimeExtend(2) +// CHECK-NEXT: ~LifetimeExtend() +// CHECK-NEXT: : 2 +// CHECK-NOT: ~LifetimeExtend() +// CHECK: LifetimeExtend(3) +// CHECK-NEXT: : 3 +// CHECK-NEXT: ~LifetimeExtend() +// CHECK-NOT: ~LifetimeExtend() + +struct LifetimeExtend { LifetimeExtend(int); ~LifetimeExtend(); }; +void test_lifetime_extended_temporaries() { + { + const LifetimeExtend &l = LifetimeExtend(1); + 1; + } + { + // No life-time extension. + const int &l = (LifetimeExtend(2), 2); + 2; + } + { + // The last one is lifetime extended. + const LifetimeExtend &l = (3, LifetimeExtend(3)); + 3; + } + // FIXME: Add tests for lifetime extension via subobject + // references (LifetimeExtend().some_member). +} + + // CHECK-LABEL: int *PR18472() // CHECK: [B2 (ENTRY)] // CHECK-NEXT: Succs (1): B1