DR1492: In a definition of a destructor, the exception specification must be
authorRichard Smith <richard-llvm@metafoo.co.uk>
Tue, 16 Oct 2012 23:30:16 +0000 (23:30 +0000)
committerRichard Smith <richard-llvm@metafoo.co.uk>
Tue, 16 Oct 2012 23:30:16 +0000 (23:30 +0000)
explicitly specified iff it was specified in the declaration.

llvm-svn: 166071

clang/lib/Sema/SemaExceptionSpec.cpp
clang/test/CXX/special/class.dtor/p3-0x.cpp
clang/test/CXX/special/class.dtor/p3.cpp [new file with mode: 0644]

index e0e0d20..e35badc 100644 (file)
@@ -120,6 +120,23 @@ Sema::ResolveExceptionSpec(SourceLocation Loc, const FunctionProtoType *FPT) {
   return SourceDecl->getType()->castAs<FunctionProtoType>();
 }
 
+/// Get the type that a function had prior to adjustment of the exception
+/// specification.
+static const FunctionProtoType *getUnadjustedFunctionType(FunctionDecl *Decl) {
+  if (isa<CXXDestructorDecl>(Decl) && Decl->getTypeSourceInfo()) {
+    const FunctionProtoType *Ty =
+      Decl->getTypeSourceInfo()->getType()->getAs<FunctionProtoType>();
+    if (!Ty->hasExceptionSpec())
+      // The type will be adjusted. Use the EST_None exception specification
+      // from the type as written.
+      return Ty;
+  }
+
+  // Use whatever type the function now has. The TypeSourceInfo does not contain
+  // an instantiated exception specification for a function template, 
+  return Decl->getType()->getAs<FunctionProtoType>();
+}
+
 bool Sema::CheckEquivalentExceptionSpec(FunctionDecl *Old, FunctionDecl *New) {
   OverloadedOperatorKind OO = New->getDeclName().getCXXOverloadedOperator();
   bool IsOperatorNew = OO == OO_New || OO == OO_Array_New;
@@ -129,16 +146,14 @@ bool Sema::CheckEquivalentExceptionSpec(FunctionDecl *Old, FunctionDecl *New) {
   if (getLangOpts().MicrosoftExt)
     DiagID = diag::warn_mismatched_exception_spec; 
 
-  if (!CheckEquivalentExceptionSpec(PDiag(DiagID),
-                                    PDiag(diag::note_previous_declaration),
-                                    Old->getType()->getAs<FunctionProtoType>(),
-                                    Old->getLocation(),
-                                    New->getType()->getAs<FunctionProtoType>(),
-                                    New->getLocation(),
-                                    &MissingExceptionSpecification,
-                                    &MissingEmptyExceptionSpecification,
-                                    /*AllowNoexceptAllMatchWithNoSpec=*/true,
-                                    IsOperatorNew))
+  // Check the types as written: they must match before any exception
+  // specification adjustment is applied.
+  if (!CheckEquivalentExceptionSpec(
+        PDiag(DiagID), PDiag(diag::note_previous_declaration),
+        getUnadjustedFunctionType(Old), Old->getLocation(),
+        getUnadjustedFunctionType(New), New->getLocation(),
+        &MissingExceptionSpecification, &MissingEmptyExceptionSpecification,
+        /*AllowNoexceptAllMatchWithNoSpec=*/true, IsOperatorNew))
     return false;
 
   // The failure was something other than an empty exception
@@ -146,8 +161,7 @@ bool Sema::CheckEquivalentExceptionSpec(FunctionDecl *Old, FunctionDecl *New) {
   if (!MissingExceptionSpecification && !MissingEmptyExceptionSpecification)
     return true;
 
-  const FunctionProtoType *NewProto 
-    = New->getType()->getAs<FunctionProtoType>();
+  const FunctionProtoType *NewProto = getUnadjustedFunctionType(New);
 
   // The new function declaration is only missing an empty exception
   // specification "throw()". If the throw() specification came from a
@@ -172,8 +186,7 @@ bool Sema::CheckEquivalentExceptionSpec(FunctionDecl *Old, FunctionDecl *New) {
   }
 
   if (MissingExceptionSpecification && NewProto) {
-    const FunctionProtoType *OldProto
-      = Old->getType()->getAs<FunctionProtoType>();
+    const FunctionProtoType *OldProto = getUnadjustedFunctionType(Old);
 
     FunctionProtoType::ExtProtoInfo EPI = NewProto->getExtProtoInfo();
     EPI.ExceptionSpecType = OldProto->getExceptionSpecType();
@@ -290,8 +303,7 @@ bool Sema::CheckEquivalentExceptionSpec(
   unsigned DiagID = diag::err_mismatched_exception_spec;
   if (getLangOpts().MicrosoftExt)
     DiagID = diag::warn_mismatched_exception_spec; 
-  return CheckEquivalentExceptionSpec(
-                                      PDiag(DiagID),
+  return CheckEquivalentExceptionSpec(PDiag(DiagID),
                                       PDiag(diag::note_previous_declaration),
                                       Old, OldLoc, New, NewLoc);
 }
index 44bf5aa..291353a 100644 (file)
@@ -45,7 +45,7 @@ G::~G() {}
 
 struct H {
   B b;
-  ~H();
+  ~H() throw(int);
 };
 H::~H() throw(int) {}
 
diff --git a/clang/test/CXX/special/class.dtor/p3.cpp b/clang/test/CXX/special/class.dtor/p3.cpp
new file mode 100644 (file)
index 0000000..c3a292d
--- /dev/null
@@ -0,0 +1,17 @@
+// RUN: %clang_cc1 -std=c++11 -fcxx-exceptions -verify %s
+
+// The exception specification of a destructor declaration is matched *before*
+// the exception specification adjustment occurs.
+namespace DR1492 {
+  struct A { ~A(); }; // expected-note {{here}}
+  A::~A() noexcept {} // expected-error {{does not match previous declaration}}
+
+  struct B { ~B() noexcept; }; // expected-note {{here}}
+  B::~B() {} // expected-warning {{~B' is missing exception specification 'noexcept'}}
+
+  template<typename T> struct C {
+    T t;
+    ~C(); // expected-note {{here}}
+  };
+  template<typename T> C<T>::~C() noexcept {} // expected-error {{does not match previous}}
+}