jyu2 created this revision.

Throwing in the destructor is not good (C++11 change try to not allow see 
below).  But in reality, those codes are exist.  
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.

With this change, the application worked before may now run into runtime 
termination.  May gold here is to emit a warning to provide only possible info 
to where the code may need to be changed.

First there is no way, in compile time to identify the “throw” really throw out 
of the function. Things like the call which throw out…  To keep this simple, 
when “throw” is seen, checking its enclosing function(only destructor and 
dealloc functions) with noexcept(true) specifier emit warning.

Here is implementation detail:
A new member function CheckCXXThrowInNonThrowingFunc is added for class Sema in 
Sema.h.  It is used in the call to both BuildCXXThrow and TransformCXXThrowExpr.

The function basic check if the enclosing function with non-throwing noexcept 
specifer, if so emit warning for it.

The example of warning message like:
k1.cpp:18:3: warning: ''~dependent_warn'' has a (possible implicit) non-throwing

      noexcept specifier. Throwing exception may cause termination.
      [-Wthrow-in-dtor]
  throw 1;
  ^

k1.cpp:43:30: note: in instantiation of member function

  'dependent_warn<noexcept_fun>::~dependent_warn' requested here

dependent_warn<noexcept_fun> f; // cause warning

Let me know if more information is needed.

Thanks.

Jennifer


https://reviews.llvm.org/D33333

Files:
  include/clang/Basic/DiagnosticGroups.td
  include/clang/Basic/DiagnosticSemaKinds.td
  include/clang/Sema/Sema.h
  lib/Sema/SemaExprCXX.cpp
  lib/Sema/TreeTransform.h
  test/SemaCXX/warn-throw-out-dtor.cpp

Index: lib/Sema/SemaExprCXX.cpp
===================================================================
--- lib/Sema/SemaExprCXX.cpp
+++ lib/Sema/SemaExprCXX.cpp
@@ -687,6 +687,37 @@
   return BuildCXXThrow(OpLoc, Ex, IsThrownVarInScope);
 }
 
+static bool isNoexcept(const FunctionDecl * FD)
+{
+  if (const FunctionProtoType *FPT = FD->getType()->castAs<FunctionProtoType>())
+    if (isNoexceptExceptionSpec(FPT->getExceptionSpecType()) &&
+        FPT->getNoexceptSpec(FD->getASTContext()) ==
+            FunctionProtoType::NR_Nothrow) 
+      return true;
+  return false;
+}
+
+static bool isNoexceptTrue(const FunctionDecl * FD)
+{
+  // Avoid emitting error twice.
+  if (const FunctionDecl * TempFD = FD->getTemplateInstantiationPattern())
+    if (isNoexcept(TempFD)) 
+      return false;
+  return isNoexcept(FD);
+}
+
+void Sema::CheckCXXThrowInNonThrowingFunc(SourceLocation OpLoc) {
+
+  if (const FunctionDecl *FD = getCurFunctionDecl())
+    if (getLangOpts().CPlusPlus11 &&
+        !getSourceManager().isInSystemHeader(OpLoc) &&
+        (isa<CXXDestructorDecl>(FD) ||
+         FD->getDeclName().getCXXOverloadedOperator() == OO_Delete ||
+         FD->getDeclName().getCXXOverloadedOperator() == OO_Array_Delete))
+      if (isNoexceptTrue(FD)) 
+        Diag(OpLoc, diag::warn_throw_in_dtor) << FD;
+}
+
 ExprResult Sema::BuildCXXThrow(SourceLocation OpLoc, Expr *Ex,
                                bool IsThrownVarInScope) {
   // Don't report an error if 'throw' is used in system headers.
@@ -702,6 +733,8 @@
   if (getCurScope() && getCurScope()->isOpenMPSimdDirectiveScope())
     Diag(OpLoc, diag::err_omp_simd_region_cannot_use_stmt) << "throw";
 
+  CheckCXXThrowInNonThrowingFunc(OpLoc);
+
   if (Ex && !Ex->isTypeDependent()) {
     QualType ExceptionObjectTy = Context.getExceptionObjectType(Ex->getType());
     if (CheckCXXThrowOperand(OpLoc, ExceptionObjectTy, Ex))
Index: lib/Sema/TreeTransform.h
===================================================================
--- lib/Sema/TreeTransform.h
+++ lib/Sema/TreeTransform.h
@@ -9873,9 +9873,10 @@
   if (SubExpr.isInvalid())
     return ExprError();
 
-  if (!getDerived().AlwaysRebuild() &&
-      SubExpr.get() == E->getSubExpr())
+  if (!getDerived().AlwaysRebuild() && SubExpr.get() == E->getSubExpr()) {
+    getSema().CheckCXXThrowInNonThrowingFunc(E->getThrowLoc());
     return E;
+  }
 
   return getDerived().RebuildCXXThrowExpr(E->getThrowLoc(), SubExpr.get(),
                                           E->isThrownVariableInScope());
Index: include/clang/Sema/Sema.h
===================================================================
--- include/clang/Sema/Sema.h
+++ include/clang/Sema/Sema.h
@@ -4967,6 +4967,9 @@
   ExprResult BuildCXXThrow(SourceLocation OpLoc, Expr *Ex,
                            bool IsThrownVarInScope);
   bool CheckCXXThrowOperand(SourceLocation ThrowLoc, QualType ThrowTy, Expr *E);
+  /// Check if throw is used in function with non-throwing noexcept 
+  /// specifier.
+  void CheckCXXThrowInNonThrowingFunc(SourceLocation OpLoc);
 
   /// ActOnCXXTypeConstructExpr - Parse construction of a specified type.
   /// Can be interpreted either as function-style casting ("int(x)")
Index: include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- include/clang/Basic/DiagnosticSemaKinds.td
+++ include/clang/Basic/DiagnosticSemaKinds.td
@@ -6331,6 +6331,10 @@
   "cannot use '%0' with exceptions disabled">;
 def err_objc_exceptions_disabled : Error<
   "cannot use '%0' with Objective-C exceptions disabled">;
+def warn_throw_in_dtor 
+    : Warning<"'%0' has a (possible implicit) non-throwing noexcept "
+              "specifier. Throwing exception may cause termination.">, 
+      InGroup<ThrowInDtor>;
 def err_seh_try_outside_functions : Error<
   "cannot use SEH '__try' in blocks, captured regions, or Obj-C method decls">;
 def err_mixing_cxx_try_seh_try : Error<
Index: include/clang/Basic/DiagnosticGroups.td
===================================================================
--- include/clang/Basic/DiagnosticGroups.td
+++ include/clang/Basic/DiagnosticGroups.td
@@ -137,6 +137,7 @@
 
 def EmptyBody : DiagGroup<"empty-body">;
 def Exceptions : DiagGroup<"exceptions">;
+def ThrowInDtor : DiagGroup<"throw-in-dtor">;
 
 def GNUEmptyInitializer : DiagGroup<"gnu-empty-initializer">;
 def GNUEmptyStruct : DiagGroup<"gnu-empty-struct">;
Index: test/SemaCXX/warn-throw-out-dtor.cpp
===================================================================
--- test/SemaCXX/warn-throw-out-dtor.cpp
+++ test/SemaCXX/warn-throw-out-dtor.cpp
@@ -0,0 +1,277 @@
+// RUN: %clang_cc1 %s  -fdelayed-template-parsing -fcxx-exceptions -fexceptions -fsyntax-only -verify -std=c++11
+struct A {
+  ~A();
+}; // implicitly noexcept(true)
+A::~A() {
+  throw 1; // expected-warning {{has a (possible implicit) non-throwing noexcept}}
+}
+struct B {
+  int i;
+  ~B() noexcept(true) {}
+};
+struct R : A {
+  B b;
+  ~R() {
+    throw 1; // expected-warning {{has a (possible implicit) non-throwing noexcept}}
+  }
+};
+
+struct M : A {
+  B b;
+  ~M() noexcept(false);
+};
+
+M::~M() noexcept(false) {
+  throw 1;
+}
+
+struct N : A {
+  B b;
+  ~N(); //implicitly noexcept(true)
+};
+
+N::~N() {
+  throw 1; // expected-warning {{has a (possible implicit) non-throwing noexcept}}
+}
+struct X : A {
+  B b;
+  ~X() noexcept {
+    throw 1; // expected-warning {{has a (possible implicit) non-throwing noexcept}}
+  }
+};
+struct Y : A {
+  B b;
+  ~Y() noexcept(true) {
+    throw 1; // expected-warning {{has a (possible implicit) non-throwing noexcept}}
+  }
+};
+struct C {
+  int i;
+  ~C() noexcept(false) {}
+};
+struct D : A {
+  C c;
+  ~D() { //implicitly noexcept(false)
+    throw 1;
+  }
+};
+struct E : A {
+  C c;
+  ~E(); //implicitly noexcept(false)
+};
+E::~E() //implicitly noexcept(false)
+{
+  throw 1;
+}
+
+template <typename T>
+class A1 {
+  T b;
+
+public:
+  ~A1() {
+    throw 1; // expected-warning {{has a (possible implicit) non-throwing noexcept}}
+  }
+};
+template <typename T>
+struct B1 {
+  T i;
+  ~B1() noexcept(true) {}
+};
+template <typename T>
+struct R1 : A1<T> //expected-note {{in instantiation of member function}}
+{
+  B1<T> b;
+  ~R1() {
+    throw 1; // expected-warning {{has a (possible implicit) non-throwing noexcept}}
+  }
+};
+template <typename T>
+struct S1 : A1<T> {
+  B1<T> b;
+  ~S1() noexcept {
+    throw 1; // expected-warning {{has a (possible implicit) non-throwing noexcept}}
+  }
+};
+void operator delete(void *ptr) noexcept {
+  throw 1; // expected-warning {{has a (possible implicit) non-throwing noexcept}}
+}
+struct except_fun {
+  static const bool i = false;
+};
+struct noexcept_fun {
+  static const bool i = true;
+};
+template <typename T>
+struct dependent_warn {
+  ~dependent_warn() noexcept(T::i) {
+    throw 1;
+  }
+};
+template <typename T>
+struct dependent_warn_noexcept {
+  ~dependent_warn_noexcept() noexcept(T::i) {
+    throw 1; // expected-warning {{has a (possible implicit) non-throwing noexcept}}
+  }
+};
+template <typename T>
+struct dependent_warn_both {
+  ~dependent_warn_both() noexcept(T::i) {
+    throw 1; // expected-warning {{has a (possible implicit) non-throwing noexcept}}
+  }
+};
+int main() {
+  R1<int> o; //expected-note {{in instantiation of member function}}
+  S1<int> b;
+  dependent_warn<except_fun> f;
+  dependent_warn_noexcept<noexcept_fun> f1; //expected-note {{in instantiation of member function}}
+  dependent_warn_both<except_fun> f2;
+  dependent_warn_both<noexcept_fun> f3; //expected-note {{in instantiation of member function}}
+}
+// RUN: %clang_cc1 %s  -fdelayed-template-parsing -fcxx-exceptions -fexceptions -fsyntax-only -verify -std=c++11 
+struct A { ~A();}; // implicitly noexcept(true)
+A::~A() 
+{ 
+  throw 1; // expected-warning {{has a (possible implicit) non-throwing noexcept}}
+}
+struct B
+{
+  int i;
+  ~B() noexcept(true) {}
+};
+struct R : A
+{
+  B b;
+  ~R()  {
+  throw 1; // expected-warning {{has a (possible implicit) non-throwing noexcept}}
+   }
+};
+
+struct M : A
+{
+  B b;
+  ~M() noexcept(false); 
+};
+
+M::~M() noexcept(false)
+{
+   throw 1; 
+}
+
+struct N : A
+{
+  B b;
+  ~N(); //implicitly noexcept(true)
+};
+
+N::~N () 
+{
+  throw 1; // expected-warning {{has a (possible implicit) non-throwing noexcept}}
+}
+struct X : A
+{
+  B b;
+  ~X() noexcept { 
+    throw 1; // expected-warning {{has a (possible implicit) non-throwing noexcept}}
+  }
+};
+struct Y : A
+{
+  B b;
+  ~Y() noexcept(true) { 
+    throw 1; // expected-warning {{has a (possible implicit) non-throwing noexcept}}
+  }
+};
+struct C
+{
+  int i;
+  ~C() noexcept(false) {}
+};
+struct D : A
+{
+  C c;
+  ~D()  {  //implicitly noexcept(false)
+    throw 1; 
+   }
+};
+struct E : A
+{
+  C c;
+  ~E();  //implicitly noexcept(false)
+};
+E::~E() //implicitly noexcept(false)
+{
+  throw 1; 
+}
+
+template <typename T>
+class A1{
+ T b;
+public:
+  ~A1() { 
+    throw 1; // expected-warning {{has a (possible implicit) non-throwing noexcept}}
+  }
+};
+template <typename T>
+struct B1 
+{
+  T i;
+  ~B1() noexcept(true) {}
+};
+template <typename T>
+struct R1 : A1 <T>  //expected-note {{in instantiation of member function}}
+{
+  B1<T> b;
+  ~R1()  { 
+    throw 1; // expected-warning {{has a (possible implicit) non-throwing noexcept}}
+   } 
+}; 
+template <typename T>
+struct S1 : A1 <T>
+{
+  B1<T> b;
+  ~S1() noexcept { 
+    throw 1; // expected-warning {{has a (possible implicit) non-throwing noexcept}}
+  } 
+};
+void operator delete(void* ptr) noexcept 
+{
+  throw 1; // expected-warning {{has a (possible implicit) non-throwing noexcept}}
+}
+struct except_fun {
+static const bool i = false;
+};
+struct noexcept_fun {
+static  const  bool i = true;
+};
+template<typename T>
+struct dependent_warn {
+  ~dependent_warn() noexcept(T::i)
+  {
+    throw 1;
+  }
+};
+template<typename T>
+struct dependent_warn_noexcept {
+  ~dependent_warn_noexcept() noexcept(T::i) 
+  {
+    throw 1; // expected-warning {{has a (possible implicit) non-throwing noexcept}}
+  }
+};
+template<typename T>
+struct dependent_warn_both {
+  ~dependent_warn_both() noexcept(T::i) 
+  {
+    throw 1; // expected-warning {{has a (possible implicit) non-throwing noexcept}}
+  }
+};
+int main()
+{
+  R1 <int> o;  //expected-note {{in instantiation of member function}}
+  S1 <int> b; 
+  dependent_warn<except_fun> f;  
+  dependent_warn_noexcept<noexcept_fun> f1; //expected-note {{in instantiation of member function}} 
+  dependent_warn_both<except_fun> f2; 
+  dependent_warn_both<noexcept_fun> f3; //expected-note {{in instantiation of member function}} 
+}
+  
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to