From d92ec474faeb6133e0d41f0de4526b22778476f2 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Thu, 1 Jul 2010 05:10:53 +0000 Subject: [PATCH] Reinstate fix for PR7526, which was failing because, now that we aren't dropping all exception specifications on destructors, the exception specifications on implicitly-declared destructors were detected as being wrong (which they were). Introduce logic to provide a proper exception-specification for implicitly-declared destructors. This also fixes PR6972. Note that the other implicitly-declared special member functions also need to get exception-specifications. I'll deal with that in a subsequent commit. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@107385 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Sema/Sema.h | 2 +- lib/Sema/SemaDecl.cpp | 2 +- lib/Sema/SemaDeclCXX.cpp | 129 ++++++++++++++++++++++++++++---- test/CodeGenCXX/destructors.cpp | 19 +++++ test/SemaCXX/destructor.cpp | 7 +- 5 files changed, 141 insertions(+), 18 deletions(-) diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index 5588db79e3..93e6a06ef8 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -2672,7 +2672,7 @@ public: QualType CheckConstructorDeclarator(Declarator &D, QualType R, FunctionDecl::StorageClass& SC); void CheckConstructor(CXXConstructorDecl *Constructor); - QualType CheckDestructorDeclarator(Declarator &D, + QualType CheckDestructorDeclarator(Declarator &D, QualType R, FunctionDecl::StorageClass& SC); bool CheckDestructor(CXXDestructorDecl *Destructor); void CheckConversionDeclarator(Declarator &D, QualType &R, diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index ba84f7c223..500d73eb9b 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -3023,7 +3023,7 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC, } else if (Name.getNameKind() == DeclarationName::CXXDestructorName) { // This is a C++ destructor declaration. if (DC->isRecord()) { - R = CheckDestructorDeclarator(D, SC); + R = CheckDestructorDeclarator(D, R, SC); NewFD = CXXDestructorDecl::Create(Context, cast(DC), diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 1c0a72bf11..62dd5ff31e 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -2588,6 +2588,65 @@ void Sema::ActOnFinishCXXMemberSpecification(Scope* S, SourceLocation RLoc, dyn_cast_or_null(TagDecl.getAs())); } +namespace { + /// \brief Helper class that collects exception specifications for + /// implicitly-declared special member functions. + class ImplicitExceptionSpecification { + ASTContext &Context; + bool AllowsAllExceptions; + llvm::SmallPtrSet ExceptionsSeen; + llvm::SmallVector Exceptions; + + public: + explicit ImplicitExceptionSpecification(ASTContext &Context) + : Context(Context), AllowsAllExceptions(false) { } + + /// \brief Whether the special member function should have any + /// exception specification at all. + bool hasExceptionSpecification() const { + return !AllowsAllExceptions; + } + + /// \brief Whether the special member function should have a + /// throw(...) exception specification (a Microsoft extension). + bool hasAnyExceptionSpecification() const { + return false; + } + + /// \brief The number of exceptions in the exception specification. + unsigned size() const { return Exceptions.size(); } + + /// \brief The set of exceptions in the exception specification. + const QualType *data() const { return Exceptions.data(); } + + /// \brief Note that + void CalledDecl(CXXMethodDecl *Method) { + // If we already know that we allow all exceptions, do nothing. + if (AllowsAllExceptions) + return; + + const FunctionProtoType *Proto + = Method->getType()->getAs(); + + // If this function can throw any exceptions, make a note of that. + if (!Proto->hasExceptionSpec() || Proto->hasAnyExceptionSpec()) { + AllowsAllExceptions = true; + ExceptionsSeen.clear(); + Exceptions.clear(); + return; + } + + // Record the exceptions in this function's exception specification. + for (FunctionProtoType::exception_iterator E = Proto->exception_begin(), + EEnd = Proto->exception_end(); + E != EEnd; ++E) + if (ExceptionsSeen.insert(Context.getCanonicalType(*E))) + Exceptions.push_back(*E); + } + }; +} + + /// AddImplicitlyDeclaredMembersToClass - Adds any implicitly-declared /// special functions, such as the default constructor, copy /// constructor, or destructor, to the given C++ class (C++ @@ -2822,10 +2881,47 @@ void Sema::AddImplicitlyDeclaredMembersToClass(Scope *S, // If a class has no user-declared destructor, a destructor is // declared implicitly. An implicitly-declared destructor is an // inline public member of its class. + + // C++ [except.spec]p14: + // An implicitly declared special member function (Clause 12) shall have + // an exception-specification. + ImplicitExceptionSpecification ExceptSpec(Context); + + // Direct base-class destructors. + for (CXXRecordDecl::base_class_iterator B = ClassDecl->bases_begin(), + BEnd = ClassDecl->bases_end(); + B != BEnd; ++B) { + if (const RecordType *BaseType = B->getType()->getAs()) + ExceptSpec.CalledDecl( + cast(BaseType->getDecl())->getDestructor(Context)); + } + + // 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( + cast(BaseType->getDecl())->getDestructor(Context)); + } + + // Field destructors. + for (RecordDecl::field_iterator F = ClassDecl->field_begin(), + FEnd = ClassDecl->field_end(); + F != FEnd; ++F) { + if (const RecordType *RecordTy + = Context.getBaseElementType(F->getType())->getAs()) + ExceptSpec.CalledDecl( + cast(RecordTy->getDecl())->getDestructor(Context)); + } + QualType Ty = Context.getFunctionType(Context.VoidTy, 0, 0, false, 0, - /*FIXME: hasExceptionSpec*/false, - false, 0, 0, FunctionType::ExtInfo()); + ExceptSpec.hasExceptionSpecification(), + ExceptSpec.hasAnyExceptionSpecification(), + ExceptSpec.size(), + ExceptSpec.data(), + FunctionType::ExtInfo()); DeclarationName Name = Context.DeclarationNames.getCXXDestructorName(ClassType); @@ -2990,9 +3086,7 @@ QualType Sema::CheckConstructorDeclarator(Declarator &D, QualType R, // Rebuild the function type "R" without any type qualifiers (in // case any of the errors above fired) and with "void" as the - // return type, since constructors don't have return types. We - // *always* have to do this, because GetTypeForDeclarator will - // put in a result type of "int" when none was specified. + // return type, since constructors don't have return types. const FunctionProtoType *Proto = R->getAs(); return Context.getFunctionType(Context.VoidTy, Proto->arg_type_begin(), Proto->getNumArgs(), @@ -3087,7 +3181,7 @@ FTIHasSingleVoidArgument(DeclaratorChunk::FunctionTypeInfo &FTI) { /// emit diagnostics and set the declarator to invalid. Even if this happens, /// will be updated to reflect a well-formed type for the destructor and /// returned. -QualType Sema::CheckDestructorDeclarator(Declarator &D, +QualType Sema::CheckDestructorDeclarator(Declarator &D, QualType R, FunctionDecl::StorageClass& SC) { // C++ [class.dtor]p1: // [...] A typedef-name that names a class is a class-name @@ -3095,11 +3189,9 @@ QualType Sema::CheckDestructorDeclarator(Declarator &D, // be used as the identifier in the declarator for a destructor // declaration. QualType DeclaratorType = GetTypeFromParser(D.getName().DestructorName); - if (isa(DeclaratorType)) { + if (isa(DeclaratorType)) Diag(D.getIdentifierLoc(), diag::err_destructor_typedef_name) << DeclaratorType; - D.setInvalidType(); - } // C++ [class.dtor]p2: // A destructor is used to destroy objects of its class type. A @@ -3113,9 +3205,10 @@ QualType Sema::CheckDestructorDeclarator(Declarator &D, if (!D.isInvalidType()) Diag(D.getIdentifierLoc(), diag::err_destructor_cannot_be) << "static" << SourceRange(D.getDeclSpec().getStorageClassSpecLoc()) - << SourceRange(D.getIdentifierLoc()); + << SourceRange(D.getIdentifierLoc()) + << FixItHint::CreateRemoval(D.getDeclSpec().getStorageClassSpecLoc()); + SC = FunctionDecl::None; - D.setInvalidType(); } if (D.getDeclSpec().hasTypeSpecifier() && !D.isInvalidType()) { // Destructors don't have return types, but the parser will @@ -3163,11 +3256,17 @@ QualType Sema::CheckDestructorDeclarator(Declarator &D, // Rebuild the function type "R" without any type qualifiers or // parameters (in case any of the errors above fired) and with // "void" as the return type, since destructors don't have return - // types. We *always* have to do this, because GetTypeForDeclarator - // will put in a result type of "int" when none was specified. - // FIXME: Exceptions! + // types. + const FunctionProtoType *Proto = R->getAs(); + if (!Proto) + return QualType(); + return Context.getFunctionType(Context.VoidTy, 0, 0, false, 0, - false, false, 0, 0, FunctionType::ExtInfo()); + Proto->hasExceptionSpec(), + Proto->hasAnyExceptionSpec(), + Proto->getNumExceptions(), + Proto->exception_begin(), + Proto->getExtInfo()); } /// CheckConversionDeclarator - Called by ActOnDeclarator to check the diff --git a/test/CodeGenCXX/destructors.cpp b/test/CodeGenCXX/destructors.cpp index 1442e37403..ef3d4b9cb1 100644 --- a/test/CodeGenCXX/destructors.cpp +++ b/test/CodeGenCXX/destructors.cpp @@ -32,6 +32,25 @@ struct C { C::~C() { } +namespace PR7526 { + extern void foo(); + struct allocator { + ~allocator() throw(); + }; + + struct allocator_derived : allocator { }; + + // CHECK: define void @_ZN6PR75269allocatorD2Ev + // CHECK: call void @__cxa_call_unexpected + allocator::~allocator() throw() { foo(); } + + // CHECK: define linkonce_odr void @_ZN6PR752617allocator_derivedD1Ev + // CHECK: call void @__cxa_call_unexpected + void foo() { + allocator_derived ad; + } +} + // PR5084 template class A1 { diff --git a/test/SemaCXX/destructor.cpp b/test/SemaCXX/destructor.cpp index ae3dc86e97..3c63c3d36a 100644 --- a/test/SemaCXX/destructor.cpp +++ b/test/SemaCXX/destructor.cpp @@ -19,7 +19,9 @@ struct D { // expected-error{{type qualifier is not allowed on this function}} \ // expected-error{{destructor cannot be declared 'static'}} \ // expected-error{{destructor cannot have any parameters}} \ - // expected-error{{destructor cannot be variadic}} + // expected-error{{destructor cannot be variadic}} \ + // expected-error{{destructor cannot have a return type}} \ + // expected-error{{'const' qualifier is not allowed on a destructor}} }; struct D2 { @@ -83,3 +85,6 @@ namespace PR6709 { template class X { T v; ~X() { ++*v; } }; void a(X x) {} } + +struct X0 { virtual ~X0() throw(); }; +struct X1 : public X0 { }; -- 2.40.0