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() {} };