Patchwork C++0x PATCH to add -Wnoexcept, fix defaulted fn exception specs

login
register
mail settings
Submitter Jason Merrill
Date June 16, 2010, 3:47 p.m.
Message ID <4C18F21E.9090207@redhat.com>
Download mbox | patch
Permalink /patch/55899/
State New
Headers show

Comments

Jason Merrill - June 16, 2010, 3:47 p.m.
Two noexcept related changes:

1) We were failing to give in-class defaulted methods the same exception 
specification as an implicit declaration, as specified in the FCD.

2) I've added a -Wnoexcept flag to help identify places where adding a 
noexcept specifier would be helpful.

Tested x86_64-pc-linux-gnu, applied to trunk.
Manuel López-Ibáñez - June 16, 2010, 5:46 p.m.
On 16 June 2010 17:47, Jason Merrill <jason@redhat.com> wrote:
> Two noexcept related changes:
>
> 1) We were failing to give in-class defaulted methods the same exception
> specification as an implicit declaration, as specified in the FCD.
>
> 2) I've added a -Wnoexcept flag to help identify places where adding a
> noexcept specifier would be helpful.
>
> Tested x86_64-pc-linux-gnu, applied to trunk.

Wnoexcept? Will this include all future warnings for noexpect expressions?

+C++ ObjC++ Var(warn_noexcept) Warning
+Warn when a noexcept expression evaluates to true even though the
expression can't actually throw

This seems inconsistent with:

+	  warning (OPT_Wnoexcept, "noexcept-expression evaluates to %<false%> "
+		   "because of a call to %qD", fn);

also I would humbly suggest:

+	  warning (OPT_Wnoexcept, "%<noexcept%> expression evaluates to %<false%> "
+		   "because of a call to %qD", fn);


Cheers,

Manuel.
Jason Merrill - June 16, 2010, 8:14 p.m.
On 06/16/2010 01:46 PM, Manuel López-Ibáñez wrote:
> Wnoexcept? Will this include all future warnings for noexpect expressions?

I imagine so.

> +C++ ObjC++ Var(warn_noexcept) Warning
> +Warn when a noexcept expression evaluates to true even though the
> expression can't actually throw

Oops, that should be false too.  Thanks for the catch.

Jason

Patch

commit d5a95e55cff9bc03308ec27f05988146d9aadb43
Author: Jason Merrill <jason@redhat.com>
Date:   Tue Jun 15 21:42:03 2010 -0400

    	* method.c (defaulted_late_check): Give the defaulted method
    	the same exception specification as the implicit declaration.

diff --git a/gcc/cp/method.c b/gcc/cp/method.c
index 97f3566..0400277 100644
--- a/gcc/cp/method.c
+++ b/gcc/cp/method.c
@@ -1022,6 +1022,15 @@  defaulted_late_check (tree fn)
       error_at (DECL_SOURCE_LOCATION (fn),
 		"does not match expected signature %qD", implicit_fn);
     }
+
+  /* 8.4.2/2: If it is explicitly defaulted on its first declaration, it is
+     implicitly considered to have the same exception-specification as if
+     it had been implicitly declared.  */
+  if (DECL_DEFAULTED_IN_CLASS_P (fn))
+    {
+      tree eh_spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (implicit_fn));
+      TREE_TYPE (fn) = build_exception_variant (TREE_TYPE (fn), eh_spec);
+    }
 }
 
 /* Returns true iff FN can be explicitly defaulted, and gives any
diff --git a/gcc/testsuite/g++.dg/cpp0x/noexcept01.C b/gcc/testsuite/g++.dg/cpp0x/noexcept01.C
index e3341d8..f314684 100644
--- a/gcc/testsuite/g++.dg/cpp0x/noexcept01.C
+++ b/gcc/testsuite/g++.dg/cpp0x/noexcept01.C
@@ -59,6 +59,14 @@  struct F
 
 SA (noexcept (F()));
 
+struct G
+{
+  G() = default;
+  ~G() = default;
+};
+
+SA (noexcept (G()));
+
 template <class T, bool b>
 void tf()
 {

commit 6ccf227626b3ac9a09f5061daf2cece776c0be7c
Author: Jason Merrill <jason@redhat.com>
Date:   Wed Jun 16 11:30:02 2010 -0400

    	* c.opt: Add -Wnoexcept.
    	* except.c (check_noexcept_r): Return the problematic function.
    	(finish_noexcept_expr): Give -Wnoexcept warning.  Add complain parm.
    	* pt.c (tsubst_copy_and_build): Pass it.
    	* parser.c (cp_parser_unary_expression): Likewise.
    	* cp-tree.h: Adjust prototype.

diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 5122e1a..0517d35 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -341,6 +341,10 @@  Wnested-externs
 C ObjC Var(warn_nested_externs) Warning
 Warn about \"extern\" declarations not at file scope
 
+Wnoexcept
+C++ ObjC++ Var(warn_noexcept) Warning
+Warn when a noexcept expression evaluates to true even though the expression can't actually throw
+
 Wnon-template-friend
 C++ ObjC++ Var(warn_nontemplate_friend) Init(1) Warning
 Warn when non-templatized friend functions are declared within a template
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index b71f8af..f63ec2e 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -4845,7 +4845,7 @@  extern tree build_exc_ptr			(void);
 extern tree build_throw				(tree);
 extern int nothrow_libfn_p			(const_tree);
 extern void check_handlers			(tree);
-extern tree finish_noexcept_expr		(tree);
+extern tree finish_noexcept_expr		(tree, tsubst_flags_t);
 extern bool nothrow_spec_p			(const_tree);
 extern bool type_noexcept_p			(const_tree);
 extern bool type_throw_all_p			(const_tree);
diff --git a/gcc/cp/except.c b/gcc/cp/except.c
index c682c8d..64f4171 100644
--- a/gcc/cp/except.c
+++ b/gcc/cp/except.c
@@ -1028,20 +1028,22 @@  check_noexcept_r (tree *tp, int *walk_subtrees ATTRIBUTE_UNUSED,
          We could use TREE_NOTHROW (t) for !TREE_PUBLIC fns, though... */
       tree fn = (code == AGGR_INIT_EXPR
 		 ? AGGR_INIT_EXPR_FN (t) : CALL_EXPR_FN (t));
+      tree type = TREE_TYPE (TREE_TYPE (fn));
+
+      STRIP_NOPS (fn);
       if (TREE_CODE (fn) == ADDR_EXPR)
 	{
 	  /* We do use TREE_NOTHROW for ABI internals like __dynamic_cast,
 	     and for C library functions known not to throw.  */
-	  tree fn2 = TREE_OPERAND (fn, 0);
-	  if (TREE_CODE (fn2) == FUNCTION_DECL
-	      && DECL_EXTERN_C_P (fn2)
-	      && (DECL_ARTIFICIAL (fn2)
-		  || nothrow_libfn_p (fn2)))
-	    return TREE_NOTHROW (fn2) ? NULL_TREE : t;
+	  fn = TREE_OPERAND (fn, 0);
+	  if (TREE_CODE (fn) == FUNCTION_DECL
+	      && DECL_EXTERN_C_P (fn)
+	      && (DECL_ARTIFICIAL (fn)
+		  || nothrow_libfn_p (fn)))
+	    return TREE_NOTHROW (fn) ? NULL_TREE : fn;
 	}
-      fn = TREE_TYPE (TREE_TYPE (fn));
-      if (!TYPE_NOTHROW_P (fn))
-	return t;
+      if (!TYPE_NOTHROW_P (type))
+	return fn;
     }
 
   return NULL_TREE;
@@ -1050,13 +1052,26 @@  check_noexcept_r (tree *tp, int *walk_subtrees ATTRIBUTE_UNUSED,
 /* Evaluate noexcept ( EXPR ).  */
 
 tree
-finish_noexcept_expr (tree expr)
+finish_noexcept_expr (tree expr, tsubst_flags_t complain)
 {
+  tree fn;
+
   if (processing_template_decl)
     return build_min (NOEXCEPT_EXPR, boolean_type_node, expr);
 
-  if (cp_walk_tree_without_duplicates (&expr, check_noexcept_r, 0))
-    return boolean_false_node;
+  fn = cp_walk_tree_without_duplicates (&expr, check_noexcept_r, 0);
+  if (fn)
+    {
+      if ((complain & tf_warning) && TREE_CODE (fn) == FUNCTION_DECL
+	  && TREE_NOTHROW (fn) && !DECL_ARTIFICIAL (fn))
+	{
+	  warning (OPT_Wnoexcept, "noexcept-expression evaluates to %<false%> "
+		   "because of a call to %qD", fn);
+	  warning (OPT_Wnoexcept, "but %q+D does not throw; perhaps "
+		   "it should be declared %<noexcept%>", fn);
+	}
+      return boolean_false_node;
+    }
   else
     return boolean_true_node;
 }
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 0a7006a..c6f8d7e 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -5883,7 +5883,7 @@  cp_parser_unary_expression (cp_parser *parser, bool address_p, bool cast_p,
 	    parser->type_definition_forbidden_message = saved_message;
 
 	    cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN);
-	    return finish_noexcept_expr (expr);
+	    return finish_noexcept_expr (expr, tf_warning_or_error);
 	  }
 
 	default:
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 55ea539..b97d3f5 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -12259,7 +12259,7 @@  tsubst_copy_and_build (tree t,
 				   /*integral_constant_expression_p=*/false);
       --cp_unevaluated_operand;
       --c_inhibit_evaluation_warnings;
-      return finish_noexcept_expr (op1);
+      return finish_noexcept_expr (op1, complain);
 
     case MODOP_EXPR:
       {
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 9e517e9..34acfbc 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -196,7 +196,7 @@  in the following sections.
 -fno-default-inline  -fvisibility-inlines-hidden @gol
 -fvisibility-ms-compat @gol
 -Wabi  -Wconversion-null  -Wctor-dtor-privacy @gol
--Wnon-virtual-dtor  -Wreorder @gol
+-Wnoexcept -Wnon-virtual-dtor  -Wreorder @gol
 -Weffc++  -Wstrict-null-sentinel @gol
 -Wno-non-template-friend  -Wold-style-cast @gol
 -Woverloaded-virtual  -Wno-pmf-conversions @gol
@@ -2282,6 +2282,14 @@  Warn when a class seems unusable because all the constructors or
 destructors in that class are private, and it has neither friends nor
 public static member functions.
 
+@item -Wnoexcept @r{(C++ and Objective-C++ only)}
+@opindex Wnoexcept
+@opindex Wno-noexcept
+Warn when a noexcept-expression evaluates to false because of a call
+to a function that does not have a non-throwing exception
+specification (i.e. @samp{throw()} or @samp{noexcept}) but is known by
+the compiler to never throw an exception.
+
 @item -Wnon-virtual-dtor @r{(C++ and Objective-C++ only)}
 @opindex Wnon-virtual-dtor
 @opindex Wno-non-virtual-dtor
diff --git a/gcc/testsuite/g++.dg/cpp0x/noexcept03.C b/gcc/testsuite/g++.dg/cpp0x/noexcept03.C
index d992245..c759f6f 100644
--- a/gcc/testsuite/g++.dg/cpp0x/noexcept03.C
+++ b/gcc/testsuite/g++.dg/cpp0x/noexcept03.C
@@ -1,5 +1,5 @@ 
 // Runtime test for noexcept-specification.
-// { dg-options "-std=c++0x" }
+// { dg-options "-std=c++0x -Wnoexcept" }
 // { dg-do run }
 
 #include <exception>
@@ -23,7 +23,7 @@  void f () noexcept (false)
 }
 
 template <class T>
-void f(T) noexcept (noexcept (T()))
+void f(T) noexcept (noexcept (T())) // { dg-warning "false" }
 {
   p();
 }
@@ -34,7 +34,7 @@  void f2(T a) noexcept (noexcept (f (a)))
   f(a);
 }
 
-struct A { A() { } };
+struct A { A() { } };		// { dg-warning "does not throw" }
 
 // throw(int) overrides noexcept(false) in either order.
 void h() throw (int, std::bad_exception);