From 0320a1d7b3305d1aef36ee68b77bebe170e1d30a Mon Sep 17 00:00:00 2001 From: Sebastian Redl Date: Wed, 18 May 2011 05:20:56 +0000 Subject: [PATCH] Implement implicit exception specifications of destructors. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@131528 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Sema/Sema.h | 7 ++ lib/Sema/SemaDecl.cpp | 20 +++- lib/Sema/SemaDeclCXX.cpp | 41 ++++++- test/CXX/special/class.dtor/p3-0x.cpp | 127 +++++++++++++++++++++ test/CodeGenCXX/cxx0x-delegating-ctors.cpp | 4 +- test/CodeGenCXX/eh.cpp | 8 +- 6 files changed, 193 insertions(+), 14 deletions(-) create mode 100644 test/CXX/special/class.dtor/p3-0x.cpp diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index d1487cc317..fe14398f1b 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -2644,6 +2644,13 @@ public: void DefineImplicitDestructor(SourceLocation CurrentLocation, CXXDestructorDecl *Destructor); + /// \brief Build an exception spec for destructors that don't have one. + /// + /// C++11 says that user-defined destructors with no exception spec get one + /// that looks as if the destructor was implicitly declared. + void AdjustDestructorExceptionSpec(CXXRecordDecl *ClassDecl, + CXXDestructorDecl *Destructor); + /// \brief Declare all inherited constructors for the given class. /// /// \param ClassDecl The class declaration into which the inherited diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 3ee1626f7e..d93e5fa563 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -4143,14 +4143,25 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC, // This is a C++ destructor declaration. if (DC->isRecord()) { R = CheckDestructorDeclarator(D, R, SC); + CXXRecordDecl *Record = cast(DC); - NewFD = CXXDestructorDecl::Create(Context, - cast(DC), + CXXDestructorDecl *NewDD = CXXDestructorDecl::Create(Context, Record, D.getSourceRange().getBegin(), NameInfo, R, TInfo, isInline, /*isImplicitlyDeclared=*/false); + NewFD = NewDD; isVirtualOkay = true; + + // If the class is complete, then we now create the implicit exception + // specification. If the class is incomplete or dependent, we can't do + // it yet. + if (getLangOptions().CPlusPlus0x && !Record->isDependentType() && + Record->getDefinition() && !Record->isBeingDefined() && + R->getAs()->getExceptionSpecType() == EST_None) { + AdjustDestructorExceptionSpec(Record, NewDD); + } + } else { Diag(D.getIdentifierLoc(), diag::err_destructor_not_member); @@ -8128,6 +8139,11 @@ void Sema::ActOnFields(Scope* S, Convs->setAccess(I, (*I)->getAccess()); if (!CXXRecord->isDependentType()) { + // Adjust user-defined destructor exception spec. + if (getLangOptions().CPlusPlus0x && + CXXRecord->hasUserDeclaredDestructor()) + AdjustDestructorExceptionSpec(CXXRecord,CXXRecord->getDestructor()); + // Add any implicitly-declared members to this class. AddImplicitlyDeclaredMembersToClass(CXXRecord); diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 9f146e9254..e308956a37 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -6222,18 +6222,18 @@ Sema::ComputeDefaultedDtorExceptionSpec(CXXRecordDecl *ClassDecl) { if (const RecordType *BaseType = B->getType()->getAs()) ExceptSpec.CalledDecl( - LookupDestructor(cast(BaseType->getDecl()))); + LookupDestructor(cast(BaseType->getDecl()))); } - + // Virtual base-class destructors. for (CXXRecordDecl::base_class_iterator B = ClassDecl->vbases_begin(), BEnd = ClassDecl->vbases_end(); B != BEnd; ++B) { if (const RecordType *BaseType = B->getType()->getAs()) ExceptSpec.CalledDecl( - LookupDestructor(cast(BaseType->getDecl()))); + LookupDestructor(cast(BaseType->getDecl()))); } - + // Field destructors. for (RecordDecl::field_iterator F = ClassDecl->field_begin(), FEnd = ClassDecl->field_end(); @@ -6241,7 +6241,7 @@ Sema::ComputeDefaultedDtorExceptionSpec(CXXRecordDecl *ClassDecl) { if (const RecordType *RecordTy = Context.getBaseElementType(F->getType())->getAs()) ExceptSpec.CalledDecl( - LookupDestructor(cast(RecordTy->getDecl()))); + LookupDestructor(cast(RecordTy->getDecl()))); } return ExceptSpec; @@ -6254,7 +6254,7 @@ CXXDestructorDecl *Sema::DeclareImplicitDestructor(CXXRecordDecl *ClassDecl) { // inline public member of its class. ImplicitExceptionSpecification Spec = - ComputeDefaultedDtorExceptionSpec(ClassDecl); + ComputeDefaultedDtorExceptionSpec(ClassDecl); FunctionProtoType::ExtProtoInfo EPI = Spec.getEPI(); // Create the actual destructor declaration. @@ -6329,6 +6329,35 @@ void Sema::DefineImplicitDestructor(SourceLocation CurrentLocation, } } +void Sema::AdjustDestructorExceptionSpec(CXXRecordDecl *classDecl, + CXXDestructorDecl *destructor) { + // C++11 [class.dtor]p3: + // A declaration of a destructor that does not have an exception- + // specification is implicitly considered to have the same exception- + // specification as an implicit declaration. + const FunctionProtoType *dtorType = destructor->getType()-> + getAs(); + if (dtorType->hasExceptionSpec()) + return; + + ImplicitExceptionSpecification exceptSpec = + ComputeDefaultedDtorExceptionSpec(classDecl); + + // Replace the destructor's type. + FunctionProtoType::ExtProtoInfo epi; + epi.ExceptionSpecType = exceptSpec.getExceptionSpecType(); + epi.NumExceptions = exceptSpec.size(); + epi.Exceptions = exceptSpec.data(); + QualType ty = Context.getFunctionType(Context.VoidTy, 0, 0, epi); + + destructor->setType(ty); + + // FIXME: If the destructor has a body that could throw, and the newly created + // spec doesn't allow exceptions, we should emit a warning, because this + // change in behavior can break conforming C++03 programs at runtime. + // However, we don't have a body yet, so it needs to be done somewhere else. +} + /// \brief Builds a statement that copies the given entity from \p From to /// \c To. /// diff --git a/test/CXX/special/class.dtor/p3-0x.cpp b/test/CXX/special/class.dtor/p3-0x.cpp new file mode 100644 index 0000000000..d4a9fdd867 --- /dev/null +++ b/test/CXX/special/class.dtor/p3-0x.cpp @@ -0,0 +1,127 @@ +// RUN: %clang_cc1 -std=c++0x -fexceptions -fcxx-exceptions -emit-llvm -o - %s | FileCheck %s + +struct A { + ~A(); +}; + +struct B { + ~B() throw(int); +}; + +struct C { + B b; + ~C() {} +}; + +struct D { + ~D() noexcept(false); +}; + +struct E { + D d; + ~E() {} +}; + +void foo() { + A a; + C c; + E e; + // CHECK: invoke void @_ZN1ED1Ev + // CHECK: invoke void @_ZN1CD1Ev + // CHECK: call void @_ZN1AD1Ev +} + +struct F { + D d; + ~F(); +}; +F::~F() noexcept(false) {} + +struct G { + D d; + ~G(); +}; +G::~G() {} + +struct H { + B b; + ~H(); +}; +H::~H() throw(int) {} + +struct I { + B b; + ~I(); +}; +I::~I() {} + +// Template variants. + +template +struct TA { + ~TA(); +}; + +template +struct TB { + ~TB() throw(int); +}; + +template +struct TC { + TB b; + ~TC() {} +}; + +template +struct TD { + ~TD() noexcept(false); +}; + +template +struct TE { + TD d; + ~TE() {} +}; + +void tfoo() { + TA a; + TC c; + TE e; + // CHECK: invoke void @_ZN2TEIiED1Ev + // CHECK: invoke void @_ZN2TCIiED1Ev + // CHECK: call void @_ZN2TAIiED1Ev +} + +template +struct TF { + TD d; + ~TF(); +}; +template +TF::~TF() noexcept(false) {} + +template +struct TG { + TD d; + ~TG(); +}; +template +TG::~TG() {} + +template +struct TH { + TB b; + ~TH(); +}; +template +TH::~TH() {} + +void tinst() { + TF f; + TG g; + TH h; +} +// CHECK: define linkonce_odr void @_ZN2THIiED1Ev +// CHECK: _ZTIi +// CHECK: __cxa_call_unexpected diff --git a/test/CodeGenCXX/cxx0x-delegating-ctors.cpp b/test/CodeGenCXX/cxx0x-delegating-ctors.cpp index f45a87c687..15c8e7f5d8 100644 --- a/test/CodeGenCXX/cxx0x-delegating-ctors.cpp +++ b/test/CodeGenCXX/cxx0x-delegating-ctors.cpp @@ -2,10 +2,10 @@ struct non_trivial { non_trivial(); - ~non_trivial(); + ~non_trivial() noexcept(false); }; non_trivial::non_trivial() {} -non_trivial::~non_trivial() {} +non_trivial::~non_trivial() noexcept(false) {} // We use a virtual base to ensure that the constructor // delegation optimization (complete->base) can't be diff --git a/test/CodeGenCXX/eh.cpp b/test/CodeGenCXX/eh.cpp index 6c44c76129..88c32724b8 100644 --- a/test/CodeGenCXX/eh.cpp +++ b/test/CodeGenCXX/eh.cpp @@ -159,7 +159,7 @@ namespace test8 { // CHECK-NEXT: bitcast // CHECK-NEXT: invoke void @_ZN5test81AC1ERKS0_( // CHECK: call i8* @__cxa_begin_catch - // CHECK-NEXT: invoke void @_ZN5test81AD1Ev( + // CHECK-NEXT: call void @_ZN5test81AD1Ev( // CHECK: call void @__cxa_end_catch() // CHECK: ret void } @@ -272,7 +272,7 @@ namespace test11 { // PR7686 namespace test12 { - struct A { ~A(); }; + struct A { ~A() noexcept(false); }; bool opaque(const A&); // CHECK: define void @_ZN6test124testEv() @@ -392,8 +392,8 @@ namespace test15 { } namespace test16 { - struct A { A(); ~A(); }; - struct B { int x; B(const A &); ~B(); }; + struct A { A(); ~A() noexcept(false); }; + struct B { int x; B(const A &); ~B() noexcept(false); }; void foo(); bool cond(); -- 2.40.0