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
diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h
index d1487cc..b6ee854 100644
--- a/include/clang/Sema/Sema.h
+++ b/include/clang/Sema/Sema.h
@@ -316,6 +316,13 @@
/// 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 @@
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 3ee1626..7c6cde2 100644
--- a/lib/Sema/SemaDecl.cpp
+++ b/lib/Sema/SemaDecl.cpp
@@ -4143,14 +4143,25 @@
// 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 @@
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 @@
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());
diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp
index 1227c4e..c6a0490 100644
--- a/lib/Sema/SemaDeclCXX.cpp
+++ b/lib/Sema/SemaDeclCXX.cpp
@@ -6214,18 +6214,18 @@
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 @@
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 @@
// 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::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.
///
diff --git a/lib/Sema/SemaExceptionSpec.cpp b/lib/Sema/SemaExceptionSpec.cpp
index f1033dc..cd58f32 100644
--- a/lib/Sema/SemaExceptionSpec.cpp
+++ b/lib/Sema/SemaExceptionSpec.cpp
@@ -701,6 +701,14 @@
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>(),
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 0000000..c2d2496
--- /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 <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() {} };
diff --git a/test/CodeGenCXX/cxx0x-delegating-ctors.cpp b/test/CodeGenCXX/cxx0x-delegating-ctors.cpp
index f45a87c..15c8e7f 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 6c44c76..88c3272 100644
--- a/test/CodeGenCXX/eh.cpp
+++ b/test/CodeGenCXX/eh.cpp
@@ -159,7 +159,7 @@
// 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 @@
// PR7686
namespace test12 {
- struct A { ~A(); };
+ struct A { ~A() noexcept(false); };
bool opaque(const A&);
// CHECK: define void @_ZN6test124testEv()
@@ -392,8 +392,8 @@
}
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 ae744e3..0000000
--- 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() {} };