Destroy arrays and ARC fields when throwing out of ctors.

Previously we were only handling non-array fields of class type.

Testcases derived from a patch by WenHan Gu.

llvm-svn: 174146
This commit is contained in:
John McCall 2013-02-01 05:11:40 +00:00
parent bc0e5c0114
commit 12cc42aa1b
5 changed files with 127 additions and 32 deletions

View File

@ -532,21 +532,6 @@ static void EmitAggMemberInitializer(CodeGenFunction &CGF,
CGF.EmitBlock(AfterFor, true);
}
namespace {
struct CallMemberDtor : EHScopeStack::Cleanup {
llvm::Value *V;
CXXDestructorDecl *Dtor;
CallMemberDtor(llvm::Value *V, CXXDestructorDecl *Dtor)
: V(V), Dtor(Dtor) {}
void Emit(CodeGenFunction &CGF, Flags flags) {
CGF.EmitCXXDestructorCall(Dtor, Dtor_Complete, /*ForVirtualBase=*/false,
/*Delegating=*/false, V);
}
};
}
static void EmitMemberInitializer(CodeGenFunction &CGF,
const CXXRecordDecl *ClassDecl,
CXXCtorInitializer *MemberInit,
@ -652,22 +637,13 @@ void CodeGenFunction::EmitInitializerForField(FieldDecl *Field,
EmitAggMemberInitializer(*this, LHS, Init, ArrayIndexVar, FieldType,
ArrayIndexes, 0);
if (!CGM.getLangOpts().Exceptions)
return;
// FIXME: If we have an array of classes w/ non-trivial destructors,
// we need to destroy in reverse order of construction along the exception
// path.
const RecordType *RT = FieldType->getAs<RecordType>();
if (!RT)
return;
CXXRecordDecl *RD = cast<CXXRecordDecl>(RT->getDecl());
if (!RD->hasTrivialDestructor())
EHStack.pushCleanup<CallMemberDtor>(EHCleanup, LHS.getAddress(),
RD->getDestructor());
}
// Ensure that we destroy this object if an exception is thrown
// later in the constructor.
QualType::DestructionKind dtorKind = FieldType.isDestructedType();
if (needsEHCleanup(dtorKind))
pushEHDestroy(dtorKind, LHS.getAddress(), FieldType);
}
/// Checks whether the given constructor is a valid subject for the

View File

@ -1242,7 +1242,18 @@ CodeGenFunction::getDestroyer(QualType::DestructionKind kind) {
llvm_unreachable("Unknown DestructionKind");
}
/// pushDestroy - Push the standard destructor for the given type.
/// pushEHDestroy - Push the standard destructor for the given type as
/// an EH-only cleanup.
void CodeGenFunction::pushEHDestroy(QualType::DestructionKind dtorKind,
llvm::Value *addr, QualType type) {
assert(dtorKind && "cannot push destructor for trivial type");
assert(needsEHCleanup(dtorKind));
pushDestroy(EHCleanup, addr, type, getDestroyer(dtorKind), true);
}
/// pushDestroy - Push the standard destructor for the given type as
/// at least a normal cleanup.
void CodeGenFunction::pushDestroy(QualType::DestructionKind dtorKind,
llvm::Value *addr, QualType type) {
assert(dtorKind && "cannot push destructor for trivial type");

View File

@ -1282,6 +1282,8 @@ public:
void pushDestroy(QualType::DestructionKind dtorKind,
llvm::Value *addr, QualType type);
void pushEHDestroy(QualType::DestructionKind dtorKind,
llvm::Value *addr, QualType type);
void pushDestroy(CleanupKind kind, llvm::Value *addr, QualType type,
Destroyer *destroyer, bool useEHCleanupForArray);
void emitDestroy(llvm::Value *addr, QualType type, Destroyer *destroyer,

View File

@ -451,3 +451,70 @@ namespace test10 {
// CHECK: invoke void @__cxa_rethrow()
// CHECK: unreachable
}
// Ensure that an exception in a constructor destroys
// already-constructed array members. PR14514
namespace test11 {
struct A {
A();
~A() {}
};
struct C {
A single;
A array[2][3];
C();
};
C::C() {
throw 0;
}
// CHECK: define void @_ZN6test111CC2Ev(
// CHECK: [[THIS:%.*]] = load [[C:%.*]]** {{%.*}}
// Construct single.
// CHECK-NEXT: [[SINGLE:%.*]] = getelementptr inbounds [[C]]* [[THIS]], i32 0, i32 0
// CHECK-NEXT: call void @_ZN6test111AC1Ev([[A:%.*]]* [[SINGLE]])
// Construct array.
// CHECK-NEXT: [[ARRAY:%.*]] = getelementptr inbounds [[C]]* [[THIS]], i32 0, i32 1
// CHECK-NEXT: [[ARRAYBEGIN:%.*]] = getelementptr inbounds [2 x [3 x [[A]]]]* [[ARRAY]], i32 0, i32 0, i32 0
// CHECK-NEXT: [[ARRAYEND:%.*]] = getelementptr inbounds [[A]]* [[ARRAYBEGIN]], i64 6
// CHECK-NEXT: br label
// CHECK: [[CUR:%.*]] = phi [[A]]* [ [[ARRAYBEGIN]], {{%.*}} ], [ [[NEXT:%.*]], {{%.*}} ]
// CHECK-NEXT: invoke void @_ZN6test111AC1Ev([[A:%.*]]* [[CUR]])
// CHECK: [[NEXT]] = getelementptr inbounds [[A]]* [[CUR]], i64 1
// CHECK-NEXT: [[DONE:%.*]] = icmp eq [[A]]* [[NEXT]], [[ARRAYEND]]
// CHECK-NEXT: br i1 [[DONE]],
// throw 0;
// CHECK: invoke void @__cxa_throw(
// Landing pad 1, from constructor in array-initialization loop:
// CHECK: landingpad
// - First, destroy already-constructed bits of array.
// CHECK: [[EMPTY:%.*]] = icmp eq [[A]]* [[ARRAYBEGIN]], [[CUR]]
// CHECK-NEXT: br i1 [[EMPTY]]
// CHECK: [[AFTER:%.*]] = phi [[A]]* [ [[CUR]], {{%.*}} ], [ [[ELT:%.*]], {{%.*}} ]
// CHECK-NEXT: [[ELT]] = getelementptr inbounds [[A]]* [[AFTER]], i64 -1
// CHECK-NEXT: invoke void @_ZN6test111AD1Ev([[A]]* [[ELT]])
// CHECK: [[DONE:%.*]] = icmp eq [[A]]* [[ELT]], [[ARRAYBEGIN]]
// CHECK-NEXT: br i1 [[DONE]],
// - Next, chain to cleanup for single.
// CHECK: br label
// Landing pad 2, from throw site.
// CHECK: landingpad
// - First, destroy all of array.
// CHECK: [[ARRAYBEGIN:%.*]] = getelementptr inbounds [2 x [3 x [[A]]]]* [[ARRAY]], i32 0, i32 0, i32 0
// CHECK-NEXT: [[ARRAYEND:%.*]] = getelementptr inbounds [[A]]* [[ARRAYBEGIN]], i64 6
// CHECK-NEXT: br label
// CHECK: [[AFTER:%.*]] = phi [[A]]* [ [[ARRAYEND]], {{%.*}} ], [ [[ELT:%.*]], {{%.*}} ]
// CHECK-NEXT: [[ELT]] = getelementptr inbounds [[A]]* [[AFTER]], i64 -1
// CHECK-NEXT: invoke void @_ZN6test111AD1Ev([[A]]* [[ELT]])
// CHECK: [[DONE:%.*]] = icmp eq [[A]]* [[ELT]], [[ARRAYBEGIN]]
// CHECK-NEXT: br i1 [[DONE]],
// - Next, chain to cleanup for single.
// CHECK: br label
// Finally, the cleanup for single.
// CHECK: invoke void @_ZN6test111AD1Ev([[A]]* [[SINGLE]])
// CHECK: br label
// CHECK: resume
// (After this is a terminate landingpad.)
}

View File

@ -1,4 +1,4 @@
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -fobjc-arc -fexceptions -fobjc-exceptions -fcxx-exceptions -fobjc-runtime-has-weak -o - %s | FileCheck %s
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -fobjc-arc -fexceptions -fobjc-exceptions -fcxx-exceptions -fobjc-runtime-has-weak -o - -fobjc-arc-exceptions %s | FileCheck %s
@class Ety;
@ -81,3 +81,42 @@ void test3(void) {
// CHECK-NEXT: [[T0:%.*]] = bitcast [[ETY]]** [[E]] to i8**
// CHECK-NEXT: call void @objc_destroyWeak(i8** [[T0]]) nounwind
// CHECK-NEXT: call void @__cxa_end_catch() nounwind
namespace test4 {
struct A {
id single;
id array[2][3];
A();
};
A::A() {
throw 0;
}
// CHECK: define void @_ZN5test41AC2Ev(
// CHECK: [[THIS:%.*]] = load [[A:%.*]]** {{%.*}}
// Construct single.
// CHECK-NEXT: [[SINGLE:%.*]] = getelementptr inbounds [[A]]* [[THIS]], i32 0, i32 0
// CHECK-NEXT: store i8* null, i8** [[SINGLE]], align 8
// Construct array.
// CHECK-NEXT: [[ARRAY:%.*]] = getelementptr inbounds [[A]]* [[THIS]], i32 0, i32 1
// CHECK-NEXT: [[T0:%.*]] = bitcast [2 x [3 x i8*]]* [[ARRAY]] to i8*
// CHECK-NEXT: call void @llvm.memset.p0i8.i64(i8* [[T0]], i8 0, i64 48, i32 8, i1 false)
// throw 0;
// CHECK: invoke void @__cxa_throw(
// Landing pad from throw site:
// CHECK: landingpad
// - First, destroy all of array.
// CHECK: [[ARRAYBEGIN:%.*]] = getelementptr inbounds [2 x [3 x i8*]]* [[ARRAY]], i32 0, i32 0, i32 0
// CHECK-NEXT: [[ARRAYEND:%.*]] = getelementptr inbounds i8** [[ARRAYBEGIN]], i64 6
// CHECK-NEXT: br label
// CHECK: [[AFTER:%.*]] = phi i8** [ [[ARRAYEND]], {{%.*}} ], [ [[ELT:%.*]], {{%.*}} ]
// CHECK-NEXT: [[ELT]] = getelementptr inbounds i8** [[AFTER]], i64 -1
// CHECK-NEXT: call void @objc_storeStrong(i8** [[ELT]], i8* null) nounwind
// CHECK-NEXT: [[DONE:%.*]] = icmp eq i8** [[ELT]], [[ARRAYBEGIN]]
// CHECK-NEXT: br i1 [[DONE]],
// - Next, destroy single.
// CHECK: call void @objc_storeStrong(i8** [[SINGLE]], i8* null) nounwind
// CHECK: br label
// CHECK: resume
}