When compiling ::delete for a class with a virtual destructor, call

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.

llvm-svn: 135021
This commit is contained in:
Douglas Gregor 2011-07-13 00:54:47 +00:00
parent ee6e776be2
commit 1c2e20d73d
2 changed files with 38 additions and 4 deletions

View 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);

View 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;
}
}