]> granicus.if.org Git - clang/commitdiff
When compiling ::delete for a class with a virtual destructor, call
authorDouglas Gregor <dgregor@apple.com>
Wed, 13 Jul 2011 00:54:47 +0000 (00:54 +0000)
committerDouglas Gregor <dgregor@apple.com>
Wed, 13 Jul 2011 00:54:47 +0000 (00:54 +0000)
the complete destructor and then invoke the global delete
operator. Previously, we would invoke the deleting destructor, which
calls the wrong delete operator. Fixes PR10341.

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

lib/CodeGen/CGExprCXX.cpp
test/CodeGenCXX/delete.cpp

index 9139eb6035d279d35bcdc8d256767519004b413d..6b1b88924aa5b35b8272686c37bcffc22768bdce 100644 (file)
@@ -1211,7 +1211,8 @@ namespace {
 static void EmitObjectDelete(CodeGenFunction &CGF,
                              const FunctionDecl *OperatorDelete,
                              llvm::Value *Ptr,
-                             QualType ElementType) {
+                             QualType ElementType,
+                             bool UseGlobalDelete) {
   // Find the destructor for the type, if applicable.  If the
   // destructor is virtual, we'll just emit the vcall and return.
   const CXXDestructorDecl *Dtor = 0;
@@ -1221,17 +1222,30 @@ static void EmitObjectDelete(CodeGenFunction &CGF,
       Dtor = RD->getDestructor();
 
       if (Dtor->isVirtual()) {
+        if (UseGlobalDelete) {
+          // If we're supposed to call the global delete, make sure we do so
+          // even if the destructor throws.
+          CGF.EHStack.pushCleanup<CallObjectDelete>(NormalAndEHCleanup,
+                                                    Ptr, OperatorDelete, 
+                                                    ElementType);
+        }
+        
         const llvm::Type *Ty =
           CGF.getTypes().GetFunctionType(CGF.getTypes().getFunctionInfo(Dtor,
                                                                Dtor_Complete),
                                          /*isVariadic=*/false);
           
         llvm::Value *Callee
-          = CGF.BuildVirtualCall(Dtor, Dtor_Deleting, Ptr, Ty);
+          = CGF.BuildVirtualCall(Dtor, 
+                                 UseGlobalDelete? Dtor_Complete : Dtor_Deleting,
+                                 Ptr, Ty);
         CGF.EmitCXXMemberCall(Dtor, Callee, ReturnValueSlot(), Ptr, /*VTT=*/0,
                               0, 0);
 
-        // The dtor took care of deleting the object.
+        if (UseGlobalDelete) {
+          CGF.PopCleanupBlock();
+        }
+        
         return;
       }
     }
@@ -1477,7 +1491,8 @@ void CodeGenFunction::EmitCXXDeleteExpr(const CXXDeleteExpr *E) {
   if (E->isArrayForm()) {
     EmitArrayDelete(*this, E, Ptr, DeleteTy);
   } else {
-    EmitObjectDelete(*this, E->getOperatorDelete(), Ptr, DeleteTy);
+    EmitObjectDelete(*this, E->getOperatorDelete(), Ptr, DeleteTy,
+                     E->isGlobalDelete());
   }
 
   EmitBlock(DeleteEnd);
index 51c860e9097729203a2f1dbe081d2f3811400793..f3586d46740ed9762595615a6692e9752b58f018 100644 (file)
@@ -112,3 +112,22 @@ namespace test3 {
     delete a;
   }
 }
+
+namespace test4 {
+  // PR10341: ::delete with a virtual destructor
+  struct X {
+    virtual ~X();
+    void operator delete (void *);
+  };
+
+  // CHECK: define void @_ZN5test421global_delete_virtualEPNS_1XE
+  void global_delete_virtual(X *xp) {
+    // CHECK: [[VTABLE:%.*]] = load void ([[X:%.*]])***
+    // CHECK-NEXT: [[VFN:%.*]] = getelementptr inbounds void ([[X]])** [[VTABLE]], i64 0
+    // CHECK-NEXT: [[VFNPTR:%.*]] = load void ([[X]])** [[VFN]]
+    // CHECK-NEXT: call void [[VFNPTR]]([[X]] [[OBJ:%.*]])
+    // CHECK-NEXT: [[OBJVOID:%.*]] = bitcast [[X]] [[OBJ]] to i8*
+    // CHECK-NEXT: call void @_ZdlPv(i8* [[OBJVOID]]) nounwind
+    ::delete xp;
+  }
+}