From 12cc42aa1b29168020a0d671314b39f7bd8a3d68 Mon Sep 17 00:00:00 2001 From: John McCall Date: Fri, 1 Feb 2013 05:11:40 +0000 Subject: [PATCH] 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 --- clang/lib/CodeGen/CGClass.cpp | 36 +++------------- clang/lib/CodeGen/CGDecl.cpp | 13 +++++- clang/lib/CodeGen/CodeGenFunction.h | 2 + clang/test/CodeGenCXX/exceptions.cpp | 67 ++++++++++++++++++++++++++++++ clang/test/CodeGenObjCXX/arc-exceptions.mm | 41 +++++++++++++++++- 5 files changed, 127 insertions(+), 32 deletions(-) diff --git a/clang/lib/CodeGen/CGClass.cpp b/clang/lib/CodeGen/CGClass.cpp index 0611ed7..ce32acd 100644 --- a/clang/lib/CodeGen/CGClass.cpp +++ b/clang/lib/CodeGen/CGClass.cpp @@ -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(); - if (!RT) - return; - - CXXRecordDecl *RD = cast(RT->getDecl()); - if (!RD->hasTrivialDestructor()) - EHStack.pushCleanup(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 diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp index ac45339..a43a383 100644 --- a/clang/lib/CodeGen/CGDecl.cpp +++ b/clang/lib/CodeGen/CGDecl.cpp @@ -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"); diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index 99bb8e3..15fd10c 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -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, diff --git a/clang/test/CodeGenCXX/exceptions.cpp b/clang/test/CodeGenCXX/exceptions.cpp index 723e8d1..06003c0 100644 --- a/clang/test/CodeGenCXX/exceptions.cpp +++ b/clang/test/CodeGenCXX/exceptions.cpp @@ -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.) +} diff --git a/clang/test/CodeGenObjCXX/arc-exceptions.mm b/clang/test/CodeGenObjCXX/arc-exceptions.mm index fb5300d..b6e0275 100644 --- a/clang/test/CodeGenObjCXX/arc-exceptions.mm +++ b/clang/test/CodeGenObjCXX/arc-exceptions.mm @@ -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 +} -- 2.7.4