]> granicus.if.org Git - clang/commitdiff
Teach the CFG builder how to properly destroy temporaries who
authorDouglas Gregor <dgregor@apple.com>
Tue, 15 Nov 2011 15:29:30 +0000 (15:29 +0000)
committerDouglas Gregor <dgregor@apple.com>
Tue, 15 Nov 2011 15:29:30 +0000 (15:29 +0000)
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 <rdar://problem/10398199>.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@144646 91177308-0d34-0410-b5e6-96231b3b80d8

lib/Analysis/CFG.cpp
test/SemaCXX/uninitialized.cpp

index 73879214f3434a59089ecc518deca5af3fdb916d..4c1652122f9b1ca1a6a10a079b03cebc8b2e8eba 100644 (file)
@@ -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<ExprWithCleanups>(Init)) {
+      Init = EWC->getSubExpr();
+      continue;
+    }
+    
+    // Skip through the temporary-materialization expression.
+    if (const MaterializeTemporaryExpr *MTE
+          = dyn_cast<MaterializeTemporaryExpr>(Init)) {
+      Init = MTE->GetTemporaryExpr();
+      continue;
+    }
+    
+    // Skip derived-to-base and no-op casts.
+    if (const CastExpr *CE = dyn_cast<CastExpr>(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<MemberExpr>(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<FunctionType>(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<ReferenceType>()) {
-    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();
index c25bd201d626c548467e3735234e75d36a171151..c3a5994af45fd1c7b96382d9577dcb6611de0e3f 100644 (file)
@@ -117,3 +117,30 @@ struct S {
 };
 
 struct C { char a[100], *e; } car = { .e = car.a };
+
+// <rdar://problem/10398199>
+namespace rdar10398199 {
+  class FooBase { protected: ~FooBase() {} };
+  class Foo : public FooBase {
+  public:
+    operator int&() const;
+  };
+  void stuff();
+  template <typename T> class FooImpl : public Foo {
+    T val;
+  public:
+    FooImpl(const T &x) : val(x) {}
+    ~FooImpl() { stuff(); }
+  };
+
+  template <typename T> FooImpl<T> makeFoo(const T& x) {
+    return FooImpl<T>(x);
+  }
+
+  void test() {
+    const Foo &x = makeFoo(42);
+    const int&y = makeFoo(42u);
+    (void)x;
+    (void)y;
+  };
+}