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.

llvm-svn: 107385
This commit is contained in:
Douglas Gregor 2010-07-01 05:10:53 +00:00
parent 42b7ee15f5
commit 957551609c
5 changed files with 141 additions and 18 deletions

View File

@ -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,

View File

@ -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),

View File

@ -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

View File

@ -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 {

View File

@ -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 { };