Reapply r121528, fixing PR9941 by delaying the exception specification check for destructors until the class is complete and destructors have been adjusted.

llvm-svn: 131632
This commit is contained in:
Sebastian Redl 2011-05-19 05:13:44 +00:00
parent 9eb5a410bd
commit 623ea82a6b
8 changed files with 236 additions and 21 deletions

View File

@ -316,6 +316,13 @@ public:
/// cycle detection at the end of the TU.
llvm::SmallVector<CXXConstructorDecl*, 4> 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<std::pair<const CXXDestructorDecl*,
const CXXDestructorDecl*>, 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

View File

@ -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<CXXRecordDecl>(DC);
NewFD = CXXDestructorDecl::Create(Context,
cast<CXXRecordDecl>(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<FunctionProtoType>()->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<ObjCIvarDecl**>(RecFields.data());

View File

@ -6214,18 +6214,18 @@ Sema::ComputeDefaultedDtorExceptionSpec(CXXRecordDecl *ClassDecl) {
if (const RecordType *BaseType = B->getType()->getAs<RecordType>())
ExceptSpec.CalledDecl(
LookupDestructor(cast<CXXRecordDecl>(BaseType->getDecl())));
LookupDestructor(cast<CXXRecordDecl>(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<RecordType>())
ExceptSpec.CalledDecl(
LookupDestructor(cast<CXXRecordDecl>(BaseType->getDecl())));
LookupDestructor(cast<CXXRecordDecl>(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<RecordType>())
ExceptSpec.CalledDecl(
LookupDestructor(cast<CXXRecordDecl>(RecordTy->getDecl())));
LookupDestructor(cast<CXXRecordDecl>(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<FunctionProtoType>();
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.
///

View File

@ -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<CXXDestructorDecl>(New)) {
// The destructor might be updated once the definition is finished. So
// remember it and check later.
DelayedDestructorExceptionSpecChecks.push_back(std::make_pair(
cast<CXXDestructorDecl>(New), cast<CXXDestructorDecl>(Old)));
return false;
}
return CheckExceptionSpecSubset(PDiag(diag::err_override_exception_spec),
PDiag(diag::note_overridden_virtual_function),
Old->getType()->getAs<FunctionProtoType>(),

View File

@ -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 <typename T>
struct TA {
~TA();
};
template <typename T>
struct TB {
~TB() throw(int);
};
template <typename T>
struct TC {
TB<T> b;
~TC() {}
};
template <typename T>
struct TD {
~TD() noexcept(false);
};
template <typename T>
struct TE {
TD<T> d;
~TE() {}
};
void tfoo() {
TA<int> a;
TC<int> c;
TE<int> e;
// CHECK: invoke void @_ZN2TEIiED1Ev
// CHECK: invoke void @_ZN2TCIiED1Ev
// CHECK: call void @_ZN2TAIiED1Ev
}
template <typename T>
struct TF {
TD<T> d;
~TF();
};
template <typename T>
TF<T>::~TF() noexcept(false) {}
template <typename T>
struct TG {
TD<T> d;
~TG();
};
template <typename T>
TG<T>::~TG() {}
template <typename T>
struct TH {
TB<T> b;
~TH();
};
template <typename T>
TH<T>::~TH() {}
void tinst() {
TF<int> f;
TG<int> g;
TH<int> 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() {} };

View File

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

View File

@ -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();

View File

@ -1,7 +0,0 @@
// RUN: %clang_cc1 -std=c++0x -fsyntax-only -verify %s
struct X
{ virtual ~X() {} };
struct Y : X
{ virtual ~Y() {} };