This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

C++ PATCH to add -Wterminate


C++11 changed destructors to be noexcept by default; as a result, code with throwing destructors that works in C++98 immediately calls terminate in C++11. When testing changing the default for cxx_dialect to C++11 I noticed that this was causing one of the tests to fail, and decided to add a warning for a throw-expression in a noexcept function without an intervening try block. This patch also adds the related warning to -Wc++11-compat.

Tested x86_64-pc-linux-gnu, applying to trunk.
commit 1ec3d94aa682212752ff3e1cef13ac118a71aa78
Author: Jason Merrill <jason@redhat.com>
Date:   Tue May 5 14:01:08 2015 -0500

    gcc/c-family/
    	* c.opt (Wterminate): New.
    gcc/cp/
    	* cp-gimplify.c (cp_genericize_r): Track TRY_BLOCK and
    	MUST_NOT_THROW_EXPR, warn about a THROW_EXPR directly within a
    	MUST_NOT_THROW_EXPR.
    	(cp_genericize_data): Add try_block field.
    	(cp_genericize_tree): Initialize it.
    	* except.c (expand_end_catch_block): Set TREE_NO_WARNING on
    	implicit rethrow.

diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 983f4a8..8ef0cea 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -829,6 +829,10 @@ Wsystem-headers
 C ObjC C++ ObjC++ Warning
 ; Documented in common.opt
 
+Wterminate
+C++ ObjC++ Warning Var(warn_terminate) Init(1)
+Warn if a throw expression will always result in a call to terminate()
+
 Wtraditional
 C ObjC CPP(cpp_warn_traditional) CppReason(CPP_W_TRADITIONAL) Var(warn_traditional) Init(0) Warning
 Warn about features not present in traditional C
diff --git a/gcc/cp/cp-gimplify.c b/gcc/cp/cp-gimplify.c
index 70645b5..35749ef 100644
--- a/gcc/cp/cp-gimplify.c
+++ b/gcc/cp/cp-gimplify.c
@@ -905,6 +905,7 @@ struct cp_genericize_data
   hash_set<tree> *p_set;
   vec<tree> bind_expr_stack;
   struct cp_genericize_omp_taskreg *omp_ctx;
+  tree try_block;
 };
 
 /* Perform any pre-gimplification lowering of C++ front end trees to
@@ -1193,6 +1194,54 @@ cp_genericize_r (tree *stmt_p, int *walk_subtrees, void *data)
       wtd->omp_ctx = omp_ctx.outer;
       splay_tree_delete (omp_ctx.variables);
     }
+  else if (TREE_CODE (stmt) == TRY_BLOCK)
+    {
+      *walk_subtrees = 0;
+      tree try_block = wtd->try_block;
+      wtd->try_block = stmt;
+      cp_walk_tree (&TRY_STMTS (stmt), cp_genericize_r, data, NULL);
+      wtd->try_block = try_block;
+      cp_walk_tree (&TRY_HANDLERS (stmt), cp_genericize_r, data, NULL);
+    }
+  else if (TREE_CODE (stmt) == MUST_NOT_THROW_EXPR)
+    {
+      /* MUST_NOT_THROW_COND might be something else with TM.  */
+      if (MUST_NOT_THROW_COND (stmt) == NULL_TREE)
+	{
+	  *walk_subtrees = 0;
+	  tree try_block = wtd->try_block;
+	  wtd->try_block = stmt;
+	  cp_walk_tree (&TREE_OPERAND (stmt, 0), cp_genericize_r, data, NULL);
+	  wtd->try_block = try_block;
+	}
+    }
+  else if (TREE_CODE (stmt) == THROW_EXPR)
+    {
+      location_t loc = location_of (stmt);
+      if (TREE_NO_WARNING (stmt))
+	/* Never mind.  */;
+      else if (wtd->try_block)
+	{
+	  if (TREE_CODE (wtd->try_block) == MUST_NOT_THROW_EXPR
+	      && warning_at (loc, OPT_Wterminate,
+			     "throw will always call terminate()")
+	      && cxx_dialect >= cxx11
+	      && DECL_DESTRUCTOR_P (current_function_decl))
+	    inform (loc, "in C++11 destructors default to noexcept");
+	}
+      else
+	{
+	  if (warn_cxx0x_compat && cxx_dialect < cxx11
+	      && DECL_DESTRUCTOR_P (current_function_decl)
+	      && (TYPE_RAISES_EXCEPTIONS (TREE_TYPE (current_function_decl))
+		  == NULL_TREE)
+	      && (get_defaulted_eh_spec (current_function_decl)
+		  == empty_except_spec))
+	    warning_at (loc, OPT_Wc__0x_compat,
+			"in C++11 this throw will terminate because "
+			"destructors default to noexcept");
+	}
+    }
   else if (TREE_CODE (stmt) == CONVERT_EXPR)
     gcc_assert (!CONVERT_EXPR_VBASE_PATH (stmt));
   else if (TREE_CODE (stmt) == FOR_STMT)
@@ -1269,6 +1318,7 @@ cp_genericize_tree (tree* t_p)
   wtd.p_set = new hash_set<tree>;
   wtd.bind_expr_stack.create (0);
   wtd.omp_ctx = NULL;
+  wtd.try_block = NULL_TREE;
   cp_walk_tree (t_p, cp_genericize_r, &wtd, NULL);
   delete wtd.p_set;
   wtd.bind_expr_stack.release ();
diff --git a/gcc/cp/except.c b/gcc/cp/except.c
index 3ff1ce6..614f2e9 100644
--- a/gcc/cp/except.c
+++ b/gcc/cp/except.c
@@ -579,7 +579,11 @@ expand_end_catch_block (void)
   if (in_function_try_handler
       && (DECL_CONSTRUCTOR_P (current_function_decl)
 	  || DECL_DESTRUCTOR_P (current_function_decl)))
-    finish_expr_stmt (build_throw (NULL_TREE));
+    {
+      tree rethrow = build_throw (NULL_TREE);
+      TREE_NO_WARNING (rethrow) = true;
+      finish_expr_stmt (rethrow);
+    }
 }
 
 tree
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index debd8ed..9c8aa99 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -2882,6 +2882,12 @@ Warn when overload resolution chooses a promotion from unsigned or
 enumerated type to a signed type, over a conversion to an unsigned type of
 the same size.  Previous versions of G++ tried to preserve
 unsignedness, but the standard mandates the current behavior.
+
+@item -Wno-terminate @r{(C++ and Objective-C++ only)}
+@opindex Wterminate
+@opindex Wno-terminate
+Disable the warning about a throw-expression that will immediately
+result in a call to @code{terminate}.
 @end table
 
 @node Objective-C and Objective-C++ Dialect Options
diff --git a/gcc/testsuite/g++.dg/compat/eh/ctor1.h b/gcc/testsuite/g++.dg/compat/eh/ctor1.h
index e83476f..9ad1860 100644
--- a/gcc/testsuite/g++.dg/compat/eh/ctor1.h
+++ b/gcc/testsuite/g++.dg/compat/eh/ctor1.h
@@ -5,6 +5,6 @@ struct Foo
 
 struct Bar
 {
-  ~Bar ();
+  ~Bar () throw(int);
   Foo f;
 };
diff --git a/gcc/testsuite/g++.dg/compat/eh/ctor1_y.C b/gcc/testsuite/g++.dg/compat/eh/ctor1_y.C
index 260ab1c..ca1cf38 100644
--- a/gcc/testsuite/g++.dg/compat/eh/ctor1_y.C
+++ b/gcc/testsuite/g++.dg/compat/eh/ctor1_y.C
@@ -7,7 +7,7 @@ Foo::~Foo()
   was_f_in_Bar_destroyed=true;
 }
 
-Bar::~Bar()
+Bar::~Bar() throw(int)
 {
   throw 1;
 }
diff --git a/gcc/testsuite/g++.dg/cpp0x/noexcept06.C b/gcc/testsuite/g++.dg/cpp0x/noexcept06.C
index 7e1db66..ad9edec 100644
--- a/gcc/testsuite/g++.dg/cpp0x/noexcept06.C
+++ b/gcc/testsuite/g++.dg/cpp0x/noexcept06.C
@@ -1,6 +1,7 @@
 // Test that checking of a nothrow specification uses the one on the
 // definition.
 // { dg-do run { target c++11 } }
+// { dg-options "-Wno-terminate" }
 
 #include <exception>
 #include <cstdlib>
diff --git a/gcc/testsuite/g++.dg/cpp0x/noexcept17.C b/gcc/testsuite/g++.dg/cpp0x/noexcept17.C
index b27acef..9ea1623 100644
--- a/gcc/testsuite/g++.dg/cpp0x/noexcept17.C
+++ b/gcc/testsuite/g++.dg/cpp0x/noexcept17.C
@@ -1,5 +1,6 @@
 // PR c++/50043
 // { dg-do compile { target c++11 } }
+// { dg-options "-Wno-terminate" }
 
 struct True1 {};
 struct True2 { ~True2(); };
diff --git a/gcc/testsuite/g++.dg/eh/cond4.C b/gcc/testsuite/g++.dg/eh/cond4.C
index a8d1cfb..4d312e4 100644
--- a/gcc/testsuite/g++.dg/eh/cond4.C
+++ b/gcc/testsuite/g++.dg/eh/cond4.C
@@ -12,7 +12,7 @@ void my_terminate ()
 
 struct A {
   A(int) { }
-  ~A() { throw 1; };
+  ~A() throw(int) { throw 1; };
 };
 struct B {
   B(A) { }
diff --git a/gcc/testsuite/g++.dg/eh/ehopt1.C b/gcc/testsuite/g++.dg/eh/ehopt1.C
index 163d76e..b2fb412 100644
--- a/gcc/testsuite/g++.dg/eh/ehopt1.C
+++ b/gcc/testsuite/g++.dg/eh/ehopt1.C
@@ -15,7 +15,7 @@ class A<int, int>
 public:
   A(int) { ++count; if (b) throw 1; }
   A(const A&) { ++count; if (b) throw 1; }
-  ~A() { --count; if (b) throw 1; }
+  ~A() throw(int) { --count; if (b) throw 1; }
 };
 
 typedef A<int, int> B;
@@ -26,7 +26,7 @@ class A<void *, void *>
 public:
   A() { if (b) throw 1; }
   A(const B&) { if (b) throw 1; }
-  ~A() { if (b) throw 1; }
+  ~A() throw(int) { if (b) throw 1; }
 };
 
 typedef A<void *, void *> C;
diff --git a/gcc/testsuite/g++.dg/eh/init-temp2.C b/gcc/testsuite/g++.dg/eh/init-temp2.C
index 6a58dda..9cf8797 100644
--- a/gcc/testsuite/g++.dg/eh/init-temp2.C
+++ b/gcc/testsuite/g++.dg/eh/init-temp2.C
@@ -8,18 +8,18 @@ template <class _Tp> class AutoPtr
 public:
   explicit AutoPtr(_Tp* __p = 0)  : _M_ptr(__p) {}
 
-  ~AutoPtr() { delete _M_ptr; }
+  ~AutoPtr() throw(int) { delete _M_ptr; }
 };
 
 struct A
 {
   A() { }
-  ~A() { throw 1.0; }
+  ~A() throw(int) { throw 1; }
 };
 
 struct B
 {
-  virtual ~B();
+  virtual ~B() throw(int);
 };
 
 B* f (const A &s) { throw 1; }
diff --git a/gcc/testsuite/g++.dg/tm/noexcept-5.C b/gcc/testsuite/g++.dg/tm/noexcept-5.C
index 4267432..e4d3f53 100644
--- a/gcc/testsuite/g++.dg/tm/noexcept-5.C
+++ b/gcc/testsuite/g++.dg/tm/noexcept-5.C
@@ -1,5 +1,5 @@
 // { dg-do compile { target c++11 } }
-// { dg-options "-fgnu-tm -O -fdump-tree-tmmark -fdump-tree-tmlower" }
+// { dg-options "-fgnu-tm -O -fdump-tree-tmmark -fdump-tree-tmlower -Wno-terminate" }
 
 int global;
 
diff --git a/gcc/testsuite/g++.dg/torture/pr49394.C b/gcc/testsuite/g++.dg/torture/pr49394.C
index 67d521f..e471885 100644
--- a/gcc/testsuite/g++.dg/torture/pr49394.C
+++ b/gcc/testsuite/g++.dg/torture/pr49394.C
@@ -4,7 +4,7 @@
 struct Mutex
 {
   bool locked;
-  ~Mutex ()
+  ~Mutex () throw(int)
   {
     if (locked)
       throw 0;
diff --git a/gcc/testsuite/g++.dg/warn/Wterminate1.C b/gcc/testsuite/g++.dg/warn/Wterminate1.C
new file mode 100644
index 0000000..affb48d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wterminate1.C
@@ -0,0 +1,20 @@
+// In C++98 mode this gets a -Wc++11-compat warning, in C++11 mode a
+// -Wterminate warning.
+
+// { dg-options "-Wall" }
+
+struct A
+{
+  ~A()
+  {
+    throw 1;			// { dg-warning "terminate" }
+  }
+};
+
+int main()
+{
+  try { A a;  }
+  catch (...) {}
+}
+
+
diff --git a/gcc/testsuite/g++.old-deja/g++.mike/eh25.C b/gcc/testsuite/g++.old-deja/g++.mike/eh25.C
index fb492f7..f3728cf 100644
--- a/gcc/testsuite/g++.old-deja/g++.mike/eh25.C
+++ b/gcc/testsuite/g++.old-deja/g++.mike/eh25.C
@@ -10,7 +10,7 @@ void my_terminate() {
 
 struct A {
   A() { }
-  ~A() {
+  ~A() throw(int) {
     std::set_terminate (my_terminate);
     throw 1;		// This throws from EH dtor, should call my_terminate
   }
diff --git a/libstdc++-v3/testsuite/30_threads/lock_guard/cons/1.cc b/libstdc++-v3/testsuite/30_threads/lock_guard/cons/1.cc
index 4219782a..6d99c27 100644
--- a/libstdc++-v3/testsuite/30_threads/lock_guard/cons/1.cc
+++ b/libstdc++-v3/testsuite/30_threads/lock_guard/cons/1.cc
@@ -27,7 +27,7 @@ struct Mutex
 {
   Mutex() : locked(false) { }
 
-  ~Mutex()
+  ~Mutex() throw(int)
   {
     if (locked)
       throw 0;
diff --git a/libstdc++-v3/testsuite/util/replacement_memory_operators.h b/libstdc++-v3/testsuite/util/replacement_memory_operators.h
index 0bfa3fc..06d955a 100644
--- a/libstdc++-v3/testsuite/util/replacement_memory_operators.h
+++ b/libstdc++-v3/testsuite/util/replacement_memory_operators.h
@@ -32,7 +32,7 @@ namespace __gnu_test
 
     counter() : _M_count(0), _M_throw(true) { }
     
-    ~counter()
+    ~counter() throw (counter_error)
     {
       if (_M_throw && _M_count != 0)
 	throw counter_error();

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]