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); @@ -1033,52 +1039,70 @@ 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(Init)) { - Init = EWC->getSubExpr(); - continue; - } - - // Skip through the temporary-materialization expression. - if (const MaterializeTemporaryExpr *MTE - = dyn_cast(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(E); + if (MTE->getStorageDuration() != SD_FullExpression) { + SmallVector CommaLHSs; + SmallVector Adjustments; + assert(MTE->GetTemporaryExpr()); + const Expr *ExtendedE = + MTE->GetTemporaryExpr()->skipRValueSubobjectAdjustments(CommaLHSs, + Adjustments); + addAutomaticObjDtorsForLifetimeExtendedTemporaries( + const_cast(ExtendedE), /*LifetimeExtended=*/true); } - - // Skip derived-to-base and no-op casts. - if (const CastExpr *CE = dyn_cast(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(E)->getTemporary()->getDestructor(); + if (Dtor->isNoReturn()) { + Block = createNoReturnBlock(); + } else { + autoCreateBlock(); } + + appendTemporaryDtor(Block, cast(E)); } - - // Skip member accesses into rvalues. - if (const MemberExpr *ME = dyn_cast(Init)) { - if (!ME->isArrow() && ME->getBase()->isRValue()) { - Init = ME->getBase(); - continue; - } + break; + default: + for (Stmt *Child : E->children()) { + if (Child) + addAutomaticObjDtorsForLifetimeExtendedTemporaries(Child, + LifetimeExtended); } - break; } +} - return Init->getType(); +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(); + + appendAutomaticObjDtor(Block, VD, S); + } + if (const Expr *Init = VD->getInit()) { + addAutomaticObjDtorsForLifetimeExtendedTemporaries( + const_cast(Init)); + } } - + /// addAutomaticObjDtors - Add to current block automatic objects destructors /// for objects in range of local scope positions. Use S as trigger statement /// for destructors. @@ -1102,22 +1126,7 @@ for (SmallVectorImpl::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); } } @@ -1215,6 +1224,55 @@ 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) { + switch (E->getStmtClass()) { + case Stmt::MaterializeTemporaryExprClass: { + const MaterializeTemporaryExpr * MTE = cast(E); + if (MTE->getStorageDuration() != SD_FullExpression) { + SmallVector CommaLHSs; + SmallVector Adjustments; + assert(MTE->GetTemporaryExpr()); + const Expr *ExtendedE = + MTE->GetTemporaryExpr()->skipRValueSubobjectAdjustments(CommaLHSs, + Adjustments); + return needsLocalScopeForType(ExtendedE->getType()); + } + } 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. @@ -1232,43 +1290,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(Init)) - Init = EWC->getSubExpr(); - if (!isa(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/cfg.cpp =================================================================== --- test/Analysis/cfg.cpp +++ test/Analysis/cfg.cpp @@ -376,6 +376,20 @@ MyClass* obj = new (buffer) MyClass[5]; } +// CHECK-LABEL: void test_lifetime_extended_temporaries() +// CHECK: [B1] + +struct LifetimeExtend { LifetimeExtend(int); ~LifetimeExtend(); int i; }; +void test_lifetime_extended_temporaries() { + // CHECK: LifetimeExtend(1) + // CHECK-NEXT: : 1 + // CHECK-NEXT: ~LifetimeExtend() + // CHECK-NOT: ~LifetimeExtend() + { + const int &i = LifetimeExtend(1).i; + 1; + } +} // CHECK-LABEL: int *PR18472() // CHECK: [B2 (ENTRY)]