jyu2 updated this revision to Diff 99802.
jyu2 added a comment.

This is new version should address all @Aaron's commands, but CFG part.

I would not think we should go that far in the compiler for this.

Thanks.


https://reviews.llvm.org/D33333

Files:
  include/clang/Basic/DiagnosticSemaKinds.td
  include/clang/Sema/Sema.h
  lib/Sema/SemaExprCXX.cpp
  lib/Sema/TreeTransform.h
  test/CXX/concepts-ts/dcl.dcl/dcl.spec/dcl.spec.concept/p1.cpp
  test/CXX/except/except.spec/p11.cpp
  test/SemaCXX/warn-throw-out_noexcept_func.cpp

Index: lib/Sema/SemaExprCXX.cpp
===================================================================
--- lib/Sema/SemaExprCXX.cpp
+++ lib/Sema/SemaExprCXX.cpp
@@ -687,6 +687,47 @@
   return BuildCXXThrow(OpLoc, Ex, IsThrownVarInScope);
 }
 
+static bool isNoexcept(const FunctionDecl * FD)
+{
+  if (const FunctionProtoType *FPT = FD->getType()->castAs<FunctionProtoType>()) 
+    if (FPT->getExceptionSpecType() != EST_None &&
+        FPT->getNoexceptSpec(FD->getASTContext()) ==
+                                   FunctionProtoType::NR_Nothrow) 
+      return true;
+  return false;
+}
+
+static bool isNoexceptTrue(const FunctionDecl * FD) {
+  // Avoid emitting error twice.
+  if (const auto * TempFD = FD->getTemplateInstantiationPattern())
+    if (isNoexcept(TempFD)) 
+      return false;
+  return isNoexcept(FD);
+}
+
+
+void Sema::CheckCXXThrowInNonThrowingFunc(SourceLocation OpLoc) {
+  bool isInCXXTryBlock = false;
+  for (Scope *S = getCurScope(); S; S = S->getParent())
+    if (S->isTryScope()) {
+      isInCXXTryBlock = true;
+      break;
+    } else if (S->isFunctionScope()) 
+      break;
+  if (const FunctionDecl *FD = getCurFunctionDecl())
+    if (!isInCXXTryBlock && !getSourceManager().isInSystemHeader(OpLoc))
+      if (isNoexceptTrue(FD)) {
+        Diag(OpLoc, diag::warn_throw_in_noexcept_func) << FD;
+        if (getLangOpts().CPlusPlus11 &&
+            (isa<CXXDestructorDecl>(FD) ||
+             FD->getDeclName().getCXXOverloadedOperator() == OO_Delete ||
+             FD->getDeclName().getCXXOverloadedOperator() == OO_Array_Delete))
+          Diag(FD->getLocation(), diag::note_throw_in_dtor);
+        else
+          Diag(FD->getLocation(), diag::note_throw_in_function);
+      }
+}
+
 ExprResult Sema::BuildCXXThrow(SourceLocation OpLoc, Expr *Ex,
                                bool IsThrownVarInScope) {
   // Don't report an error if 'throw' is used in system headers.
@@ -702,6 +743,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 a 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,15 @@
   "cannot use '%0' with exceptions disabled">;
 def err_objc_exceptions_disabled : Error<
   "cannot use '%0' with Objective-C exceptions disabled">;
+def warn_throw_in_noexcept_func 
+    : Warning<"%0 has a non-throwing exception specification but can still "
+      "throw, may result in unexpected program termination.">, 
+      InGroup<Exceptions>;
+def note_throw_in_dtor 
+    : Note<"destructor or deallocator has a (possible implicit) non-throwing "
+      "excepton specification">;
+def note_throw_in_function 
+    : Note<"nonthrowing function declare here">;
 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: test/SemaCXX/warn-throw-out_noexcept_func.cpp
===================================================================
--- test/SemaCXX/warn-throw-out_noexcept_func.cpp
+++ test/SemaCXX/warn-throw-out_noexcept_func.cpp
@@ -0,0 +1,149 @@
+// RUN: %clang_cc1 %s  -fdelayed-template-parsing -fcxx-exceptions -fexceptions -fsyntax-only -Wexceptions -verify -std=c++11
+struct A {
+  ~A();
+};         // implicitly noexcept(true)
+A::~A() {  // expected-note  {{destructor or deallocator has a}}
+  throw 1; // expected-warning {{has a non-throwing exception specification but}}
+}
+struct B {
+  int i;
+  ~B() noexcept(true) {}
+};
+struct R : A {
+  B b;
+  ~R() {     // expected-note  {{destructor or deallocator has a}}
+    throw 1; // expected-warning {{has a non-throwing exception specification but}}
+  }
+};
+
+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() {  // expected-note  {{destructor or deallocator has a}}
+  throw 1; // expected-warning {{has a non-throwing exception specification but}}
+}
+struct X : A {
+  B b;
+  ~X() noexcept { // expected-note  {{destructor or deallocator has a}}
+    throw 1; // expected-warning {{has a non-throwing exception specification but}}
+  }
+};
+struct Y : A {
+  B b;
+  ~Y() noexcept(true) { // expected-note  {{destructor or deallocator has a}}
+    throw 1; // expected-warning {{has a non-throwing exception specification but}}
+  }
+};
+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() {    // expected-note  {{destructor or deallocator has a}}
+    throw 1; // expected-warning {{has a non-throwing exception specification but}}
+  }
+};
+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() {    // expected-note  {{destructor or deallocator has a}}
+    throw 1; // expected-warning {{has a non-throwing exception specification but}}
+  }
+};
+template <typename T>
+struct S1 : A1<T> {
+  B1<T> b;
+  ~S1() noexcept { // expected-note  {{destructor or deallocator has a}}
+    throw 1; // expected-warning {{has a non-throwing exception specification but}}
+  }
+};
+void operator delete(void *ptr) noexcept { // expected-note  {{destructor or deallocator has a}}
+  throw 1; // expected-warning {{has a non-throwing exception specification but}}
+}
+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) { // expected-note  {{destructor or deallocator has a}}
+    throw 1; // expected-warning {{has a non-throwing exception specification but}}
+  }
+};
+template <typename T>
+struct dependent_warn_both {
+  ~dependent_warn_both() noexcept(T::i) { // expected-note  {{destructor or deallocator has a}}
+    throw 1; // expected-warning {{has a non-throwing exception specification but}}
+  }
+};
+void foo() noexcept { //expected-note {{nonthrowing function declare here}}
+  throw 1; // expected-warning {{has a non-throwing exception specification but}}
+}
+void bar() noexcept {
+  try {
+    throw 1;
+  } catch (...) {
+  }
+}
+#define NOEXCEPT noexcept
+void with_macro() NOEXCEPT { //expected-note {{nonthrowing function declare here}}
+  throw 1; // expected-warning {{has a non-throwing exception specification but}}
+}
+
+void with_try_block() try {
+  throw 2;
+} catch(...) {
+}
+
+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}}
+}
Index: test/CXX/concepts-ts/dcl.dcl/dcl.spec/dcl.spec.concept/p1.cpp
===================================================================
--- test/CXX/concepts-ts/dcl.dcl/dcl.spec/dcl.spec.concept/p1.cpp
+++ test/CXX/concepts-ts/dcl.dcl/dcl.spec/dcl.spec.concept/p1.cpp
@@ -6,7 +6,9 @@
   template<typename T> concept bool C2 = true;
 }
 
-template<typename T> concept bool C3() { return (throw 0, true); }
+template<typename T> concept bool C3() { // expected-note {{nonthrowing function declare here}} 
+  return (throw 0, // expected-warning {{has a non-throwing exception specification but}} 
+          true); }
 static_assert(noexcept(C3<int>()), "function concept should be treated as if noexcept(true) specified");
 
 template<typename T> concept bool D1(); // expected-error {{function concept declaration must be a definition}}
Index: test/CXX/except/except.spec/p11.cpp
===================================================================
--- test/CXX/except/except.spec/p11.cpp
+++ test/CXX/except/except.spec/p11.cpp
@@ -2,7 +2,7 @@
 // expected-no-diagnostics
 
 // This is the "let the user shoot themselves in the foot" clause.
-void f() noexcept {
+void f() noexcept(false) { 
   throw 0; // no-error
 }
 void g() throw() {
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to