Patchwork [C++,RFC,/] Implementing "Deducing "noexcept" for destructors"

login
register
mail settings
Submitter Paolo Carlini
Date March 29, 2012, 10:26 p.m.
Message ID <4F74E19C.9040903@oracle.com>
Download mbox | patch
Permalink /patch/149507/
State New
Headers show

Comments

Paolo Carlini - March 29, 2012, 10:26 p.m.
On 03/29/2012 09:27 PM, Jason Merrill wrote:
> On 03/29/2012 03:06 PM, Paolo Carlini wrote:
>>> The exception specification on old_decl doesn't matter; we can drop
>>> that test.
>> I seem to remember something going wrong with templates otherwise,
>> because implicitly_declare_fn has gcc_assert (!dependent_type_p (type));
>
> We shouldn't be doing this for templates anyway, as in general we 
> can't know what the implicitly declared function will look like.
Oh my, as simple as the below appears to work!

I simply added a !processing_template_decl check. Then I removed the 
deduce_noexcept_on_destructor calls in register_specialization and when 
I found a proper place in grokfndecl (must be before 
check_explicit_specialization) I noticed that apparently I can remove 
the other deduce_noexcept_on_destructor call which I had later on in 
grokfndecl. Thus the below passes the (updated) testsuite on x86_64-linux.

I remark (once more) that whereas we accept (otherwise nothing works in, 
eg, the library):

template<typename T>
struct A
{
   ~A();
};

template<typename T>
A<T>::~A() { }

we reject, with a "different exception specifier" error, both:

template<typename T>
struct A
{
   ~A() noexcept;
};

template<typename T>
A<T>::~A() { }

and:

template<typename T>
struct A
{
   ~A();
};

template<typename T>
A<T>::~A() noexcept { }

Over the last days I wasted a lot of time trying painfully to not reject 
either, but actually now I'm pretty sure that we are right to reject the 
former (there are exception specifiers on the declaration thus automatic 
deduction should not trigger at all) and probably also the latter. These 
cases, characterized by different situations on declaration and 
definition, confused me quite a bit...

Anyway, I'm attaching the last iteration.

Thanks again for all your help!
Paolo.

///////////////////////

Patch

Index: class.c
===================================================================
--- class.c	(revision 185977)
+++ class.c	(working copy)
@@ -4321,6 +4321,41 @@  clone_constructors_and_destructors (tree t)
     clone_function_decl (OVL_CURRENT (fns), /*update_method_vec_p=*/1);
 }
 
+/* Deduce noexcept for a destructor DTOR.  */
+
+void
+deduce_noexcept_on_destructor (tree dtor)
+{
+  if (!TYPE_RAISES_EXCEPTIONS (TREE_TYPE (dtor)))
+    {
+      tree ctx = DECL_CONTEXT (dtor);
+      tree implicit_fn = implicitly_declare_fn (sfk_destructor, ctx,
+						/*const_p=*/false);
+      tree eh_spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (implicit_fn));
+      TREE_TYPE (dtor) = build_exception_variant (TREE_TYPE (dtor), eh_spec);
+    }
+}
+
+/* For each destructor in T, deduce noexcept:
+
+   12.4/3: 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 (15.4).  */
+
+static void
+deduce_noexcept_on_destructors (tree t)
+{
+  tree fns;
+
+  /* If for some reason we don't have a CLASSTYPE_METHOD_VEC, we bail
+     out now.  */
+  if (!CLASSTYPE_METHOD_VEC (t))
+    return;
+
+  for (fns = CLASSTYPE_DESTRUCTORS (t); fns; fns = OVL_NEXT (fns))
+    deduce_noexcept_on_destructor (OVL_CURRENT (fns));
+}
+
 /* Subroutine of set_one_vmethod_tm_attributes.  Search base classes
    of TYPE for virtual functions which FNDECL overrides.  Return a
    mask of the tm attributes found therein.  */
@@ -4994,6 +5029,10 @@  check_bases_and_members (tree t)
   cant_have_const_ctor = 0;
   no_const_asn_ref = 0;
 
+  /* Deduce noexcept on destructors.  */
+  if (cxx_dialect >= cxx0x)
+    deduce_noexcept_on_destructors (t);
+
   /* Check all the base-classes.  */
   check_bases (t, &cant_have_const_ctor,
 	       &no_const_asn_ref);
Index: decl.c
===================================================================
--- decl.c	(revision 185977)
+++ decl.c	(working copy)
@@ -7448,6 +7448,12 @@  grokfndecl (tree ctype,
   if (ctype != NULL_TREE)
     grokclassfn (ctype, decl, flags);
 
+  /* 12.4/3  */
+  if (cxx_dialect >= cxx0x
+      && DECL_DESTRUCTOR_P (decl)
+      && !processing_template_decl)
+    deduce_noexcept_on_destructor (decl);
+
   decl = check_explicit_specialization (orig_declarator, decl,
 					template_count,
 					2 * funcdef_flag +
Index: method.c
===================================================================
--- method.c	(revision 185977)
+++ method.c	(working copy)
@@ -1444,7 +1444,7 @@  explain_implicit_non_constexpr (tree decl)
    reference argument or a non-const reference.  Returns the
    FUNCTION_DECL for the implicitly declared function.  */
 
-static tree
+tree
 implicitly_declare_fn (special_function_kind kind, tree type, bool const_p)
 {
   tree fn;
Index: cp-tree.h
===================================================================
--- cp-tree.h	(revision 185977)
+++ cp-tree.h	(working copy)
@@ -4978,6 +4978,7 @@  extern void fixup_attribute_variants		(tree);
 extern tree* decl_cloned_function_p		(const_tree, bool);
 extern void clone_function_decl			(tree, int);
 extern void adjust_clone_args			(tree);
+extern void deduce_noexcept_on_destructor       (tree);
 
 /* in cvt.c */
 extern tree convert_to_reference		(tree, tree, int, int, tree);
@@ -5264,6 +5265,8 @@  extern tree get_copy_assign			(tree);
 extern tree get_default_ctor			(tree);
 extern tree get_dtor				(tree, tsubst_flags_t);
 extern tree locate_ctor				(tree);
+extern tree implicitly_declare_fn               (special_function_kind, tree,
+						 bool);
 
 /* In optimize.c */
 extern bool maybe_clone_body			(tree);