From: Douglas Gregor Date: Tue, 15 Nov 2011 15:29:30 +0000 (+0000) Subject: Teach the CFG builder how to properly destroy temporaries who X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=2d9eb21325a0854e86de7344f54a8e6cef2a97ad;p=clang Teach the CFG builder how to properly destroy temporaries who lifetimes have been extended via reference binding. The type of the reference and the type of the temporary are not necessarily the same, which could cause a crash. Fixes . git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@144646 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/Analysis/CFG.cpp b/lib/Analysis/CFG.cpp index 73879214f3..4c1652122f 100644 --- a/lib/Analysis/CFG.cpp +++ b/lib/Analysis/CFG.cpp @@ -639,6 +639,52 @@ CFGBlock *CFGBuilder::addInitializer(CXXCtorInitializer *I) { 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; + } + + // 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; + } + } + + // Skip member accesses into rvalues. + if (const MemberExpr *ME = dyn_cast(Init)) { + if (!ME->isArrow() && ME->getBase()->isRValue()) { + Init = ME->getBase(); + continue; + } + } + + break; + } + + return Init->getType(); +} + /// addAutomaticObjDtors - Add to current block automatic objects destructors /// for objects in range of local scope positions. Use S as trigger statement /// for destructors. @@ -667,9 +713,13 @@ void CFGBuilder::addAutomaticObjDtors(LocalScope::const_iterator B, // 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().getNonReferenceType(); - if (const ArrayType *AT = Context->getAsArrayType(Ty)) - Ty = AT->getElementType(); + QualType Ty; + if ((*I)->getType()->isReferenceType()) { + Ty = getReferenceInitTemporaryType(*Context, (*I)->getInit()); + } else { + Ty = Context->getBaseElementType((*I)->getType()); + } + const CXXDestructorDecl *Dtor = Ty->getAsCXXRecordDecl()->getDestructor(); if (cast(Dtor->getType())->getNoReturnAttr()) Block = createNoReturnBlock(); @@ -799,16 +849,15 @@ LocalScope* CFGBuilder::addLocalScopeForVarDecl(VarDecl *VD, // Check for const references bound to temporary. Set type to pointee. QualType QT = VD->getType(); - if (const ReferenceType* RT = QT.getTypePtr()->getAs()) { - QT = RT->getPointeeType(); - if (!QT.isConstQualified()) - return Scope; + if (QT.getTypePtr()->isReferenceType()) { if (!VD->extendsLifetimeOfTemporary()) return Scope; + + QT = getReferenceInitTemporaryType(*Context, VD->getInit()); } // Check for constant size array. Set type to array element type. - if (const ConstantArrayType *AT = Context->getAsConstantArrayType(QT)) { + while (const ConstantArrayType *AT = Context->getAsConstantArrayType(QT)) { if (AT->getSize() == 0) return Scope; QT = AT->getElementType(); diff --git a/test/SemaCXX/uninitialized.cpp b/test/SemaCXX/uninitialized.cpp index c25bd201d6..c3a5994af4 100644 --- a/test/SemaCXX/uninitialized.cpp +++ b/test/SemaCXX/uninitialized.cpp @@ -117,3 +117,30 @@ struct S { }; struct C { char a[100], *e; } car = { .e = car.a }; + +// +namespace rdar10398199 { + class FooBase { protected: ~FooBase() {} }; + class Foo : public FooBase { + public: + operator int&() const; + }; + void stuff(); + template class FooImpl : public Foo { + T val; + public: + FooImpl(const T &x) : val(x) {} + ~FooImpl() { stuff(); } + }; + + template FooImpl makeFoo(const T& x) { + return FooImpl(x); + } + + void test() { + const Foo &x = makeFoo(42); + const int&y = makeFoo(42u); + (void)x; + (void)y; + }; +}