aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDouglas Gregor <dgregor@apple.com>2010-07-01 05:10:53 +0000
committerDouglas Gregor <dgregor@apple.com>2010-07-01 05:10:53 +0000
commitd92ec474faeb6133e0d41f0de4526b22778476f2 (patch)
tree8a0cff7f5091efc1b3029cac9d4eb14fa9a14cb6
parent92bc027496b2090498f31cd8278d78720a39c020 (diff)
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
-rw-r--r--lib/Sema/Sema.h2
-rw-r--r--lib/Sema/SemaDecl.cpp2
-rw-r--r--lib/Sema/SemaDeclCXX.cpp129
-rw-r--r--test/CodeGenCXX/destructors.cpp19
-rw-r--r--test/SemaCXX/destructor.cpp7
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<CXXRecordDecl>(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<CXXRecordDecl>(TagDecl.getAs<Decl>()));
}
+namespace {
+ /// \brief Helper class that collects exception specifications for
+ /// implicitly-declared special member functions.
+ class ImplicitExceptionSpecification {
+ ASTContext &Context;
+ bool AllowsAllExceptions;
+ llvm::SmallPtrSet<CanQualType, 4> ExceptionsSeen;
+ llvm::SmallVector<QualType, 4> 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<FunctionProtoType>();
+
+ // 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<RecordType>())
+ ExceptSpec.CalledDecl(
+ cast<CXXRecordDecl>(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<RecordType>())
+ ExceptSpec.CalledDecl(
+ cast<CXXRecordDecl>(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<RecordType>())
+ ExceptSpec.CalledDecl(
+ cast<CXXRecordDecl>(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<FunctionProtoType>();
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<TypedefType>(DeclaratorType)) {
+ if (isa<TypedefType>(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<FunctionProtoType>();
+ 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<typename T>
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 T> class X { T v; ~X() { ++*v; } };
void a(X<int> x) {}
}
+
+struct X0 { virtual ~X0() throw(); };
+struct X1 : public X0 { };