From 0ee33912f8ec3453856c8a32ed2c2e8007bed614 Mon Sep 17 00:00:00 2001 From: Sebastian Redl Date: Thu, 19 May 2011 05:13:44 +0000 Subject: [PATCH] Reapply r121528, fixing PR9941 by delaying the exception specification check for destructors until the class is complete and destructors have been adjusted. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@131632 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Sema/Sema.h | 14 ++ lib/Sema/SemaDecl.cpp | 33 ++++- lib/Sema/SemaDeclCXX.cpp | 41 +++++- lib/Sema/SemaExceptionSpec.cpp | 8 ++ test/CXX/special/class.dtor/p3-0x.cpp | 142 +++++++++++++++++++++ test/CodeGenCXX/cxx0x-delegating-ctors.cpp | 4 +- test/CodeGenCXX/eh.cpp | 8 +- test/SemaCXX/pr9941.cpp | 7 - 8 files changed, 236 insertions(+), 21 deletions(-) create mode 100644 test/CXX/special/class.dtor/p3-0x.cpp delete mode 100644 test/SemaCXX/pr9941.cpp diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index d1487cc317..b6ee854a3b 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -316,6 +316,13 @@ public: /// cycle detection at the end of the TU. llvm::SmallVector DelegatingCtorDecls; + /// \brief All the overriding destructors seen during a class definition + /// (there could be multiple due to nested classes) that had their exception + /// spec checks delayed, plus the overridden destructor. + llvm::SmallVector, 2> + DelayedDestructorExceptionSpecChecks; + /// \brief Callback to the parser to parse templated functions when needed. typedef void LateTemplateParserCB(void *P, const FunctionDecl *FD); LateTemplateParserCB *LateTemplateParser; @@ -2644,6 +2651,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..7c6cde2296 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); @@ -8176,6 +8192,19 @@ void Sema::ActOnFields(Scope* S, if (!Completed) Record->completeDefinition(); + + // Now that the record is complete, do any delayed exception spec checks + // we were missing. + if (!DelayedDestructorExceptionSpecChecks.empty()) { + const CXXDestructorDecl *Dtor = + DelayedDestructorExceptionSpecChecks.back().first; + if (Dtor->getParent() == Record) { + CheckOverridingFunctionExceptionSpec(Dtor, + DelayedDestructorExceptionSpecChecks.back().second); + DelayedDestructorExceptionSpecChecks.pop_back(); + } + } + } else { ObjCIvarDecl **ClsFields = reinterpret_cast(RecFields.data()); diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 1227c4eff2..c6a0490c24 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -6214,18 +6214,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(); @@ -6233,7 +6233,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; @@ -6246,7 +6246,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. @@ -6321,6 +6321,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/lib/Sema/SemaExceptionSpec.cpp b/lib/Sema/SemaExceptionSpec.cpp index f1033dc8c2..cd58f32fd4 100644 --- a/lib/Sema/SemaExceptionSpec.cpp +++ b/lib/Sema/SemaExceptionSpec.cpp @@ -701,6 +701,14 @@ bool Sema::CheckExceptionSpecCompatibility(Expr *From, QualType ToType) bool Sema::CheckOverridingFunctionExceptionSpec(const CXXMethodDecl *New, const CXXMethodDecl *Old) { + if (getLangOptions().CPlusPlus0x && New->getParent()->isBeingDefined() && + isa(New)) { + // The destructor might be updated once the definition is finished. So + // remember it and check later. + DelayedDestructorExceptionSpecChecks.push_back(std::make_pair( + cast(New), cast(Old))); + return false; + } return CheckExceptionSpecSubset(PDiag(diag::err_override_exception_spec), PDiag(diag::note_overridden_virtual_function), Old->getType()->getAs(), 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..c2d2496beb --- /dev/null +++ b/test/CXX/special/class.dtor/p3-0x.cpp @@ -0,0 +1,142 @@ +// 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 + +struct VX +{ virtual ~VX() {} }; + +struct VY : VX +{ virtual ~VY() {} }; + + +struct VA { + B b; + virtual ~VA() {} +}; + +struct VB : VA +{ virtual ~VB() {} }; 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(); diff --git a/test/SemaCXX/pr9941.cpp b/test/SemaCXX/pr9941.cpp deleted file mode 100644 index ae744e3631..0000000000 --- a/test/SemaCXX/pr9941.cpp +++ /dev/null @@ -1,7 +0,0 @@ -// RUN: %clang_cc1 -std=c++0x -fsyntax-only -verify %s - -struct X -{ virtual ~X() {} }; - -struct Y : X -{ virtual ~Y() {} }; -- 2.40.0