Re: [C++ RFC / Patch] Implementing Deducing noexcept for destructors
On 03/30/2012 09:34 PM, Paolo Carlini wrote: Thus I'm adding a !TYPE_BEING_DEFINED (DECL_CONTEXT (decl)) check Sounds good. we reject, with a different exception specifier error, both: templatetypename T struct A { ~A() noexcept; }; templatetypename T AT::~A() { } and: templatetypename T struct A { ~A(); }; templatetypename T AT::~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. I'm OK with that, as long as we correctly allow struct B { ~B() noexcept; }; B::~B() { } and struct B { ~B(); }; B::~B() noexcept { } The patch is OK. Jason
Re: [C++ RFC / Patch] Implementing Deducing noexcept for destructors
Hi, we reject, with a different exception specifier error, both: templatetypename T struct A { ~A() noexcept; }; templatetypename T AT::~A() { } and: templatetypename T struct A { ~A(); }; templatetypename T AT::~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. I'm OK with that, as long as we correctly allow struct B { ~B() noexcept; }; B::~B() { } and struct B { ~B(); }; B::~B() noexcept { } Agreed. Thanks for asking on the reflector. The patch is OK. Thanks again, the patch is in with the attached ChangeLog. Paolo. /cp 2012-04-01 Paolo Carlini paolo.carl...@oracle.com PR c++/50043 * class.c (deduce_noexcept_on_destructor, deduce_noexcept_on_destructors): New. (check_bases_and_members): Call the latter. * decl.c (grokfndecl): Call the former. * method.c (implicitly_declare_fn): Not static. * cp-tree.h (deduce_noexcept_on_destructor, implicitly_declare_fn): Declare /testsuite 2012-04-01 Paolo Carlini paolo.carl...@oracle.com PR c++/50043 * g++.dg/cpp0x/noexcept17.C: New. * g++.old-deja/g++.eh/cleanup1.C: Adjust. * g++.dg/tree-ssa/ehcleanup-1.C: Likewise. * g++.dg/cpp0x/noexcept01.C: Likewise. * g++.dg/eh/init-temp1.C: Likewise. * g++.dg/eh/ctor1.C: Likwise.
Re: [C++ RFC / Patch] Implementing Deducing noexcept for destructors
Hi again, On 03/30/2012 12:26 AM, Paolo Carlini wrote: 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. Sorry for essentially self-replying, but today, while I was traveling, I reviewed in my mind your comments over the last days, and I think I had a buglet in the patch which I sent in the last message: it doesn't make sure, in grokfndecl, to *not* call deduce_noexcept_on_destructor on a destructor of a class still being defined. Thus I'm adding a !TYPE_BEING_DEFINED (DECL_CONTEXT (decl)) check and the complete patch (which I'm attaching below) still passes testing. I also double checked that, for a simple case like: struct A { ~A(); }; A::~A() { } we process the declaration from check_bases_and_members and then the definition from grokfndecl. Thanks, Paolo. Index: testsuite/g++.old-deja/g++.eh/cleanup1.C === --- testsuite/g++.old-deja/g++.eh/cleanup1.C(revision 185982) +++ testsuite/g++.old-deja/g++.eh/cleanup1.C(working copy) @@ -2,6 +2,12 @@ // Bug: obj gets destroyed twice because the fixups for the return are // inside its cleanup region. +#ifdef __GXX_EXPERIMENTAL_CXX0X__ +#define NOEXCEPT_FALSE noexcept (false) +#else +#define NOEXCEPT_FALSE +#endif + extern C int printf (const char *, ...); int d; @@ -9,7 +15,7 @@ int d; struct myExc { }; struct myExcRaiser { - ~myExcRaiser() { throw myExc(); } + ~myExcRaiser() NOEXCEPT_FALSE { throw myExc(); } }; struct stackObj { Index: testsuite/g++.dg/tree-ssa/ehcleanup-1.C === --- testsuite/g++.dg/tree-ssa/ehcleanup-1.C (revision 185982) +++ testsuite/g++.dg/tree-ssa/ehcleanup-1.C (working copy) @@ -1,9 +1,16 @@ // { dg-options -O2 -fdump-tree-ehcleanup1-details } + +#ifdef __GXX_EXPERIMENTAL_CXX0X__ +#define NOEXCEPT_FALSE noexcept (false) +#else +#define NOEXCEPT_FALSE +#endif + extern void can_throw (); class a { public: - ~a () + ~a () NOEXCEPT_FALSE { if (0) can_throw (); Index: testsuite/g++.dg/cpp0x/noexcept17.C === --- testsuite/g++.dg/cpp0x/noexcept17.C (revision 0) +++ testsuite/g++.dg/cpp0x/noexcept17.C (revision 0) @@ -0,0 +1,54 @@ +// PR c++/50043 +// { dg-options -std=c++11 } + +struct True1 {}; +struct True2 { ~True2(); }; +struct True3 { ~True3(){ throw 0; } }; +struct False { ~False() noexcept(false); }; + +template typename Base +struct A : Base +{ +}; + +template typename Member +struct B +{ +Member mem; +}; + +template typename Base, typename Member +struct C : Base +{ +Member mem; +}; + +#define SA(X) static_assert(X, #X) + +SA( noexcept(True1())); +SA( noexcept(True2())); +SA( noexcept(True3())); +SA(!noexcept(False())); + +SA( noexcept(ATrue1())); +SA( noexcept(ATrue2())); +SA( noexcept(ATrue3())); +SA(!noexcept(AFalse())); + +SA( noexcept(BTrue1())); +SA( noexcept(BTrue2())); +SA( noexcept(BTrue3())); +SA(!noexcept(BFalse())); + +SA( noexcept(CTrue1, True2())); +SA( noexcept(CTrue1, True3())); +SA( noexcept(CTrue2, True3())); +SA( noexcept(CTrue2, True1())); +SA( noexcept(CTrue3, True1())); +SA( noexcept(CTrue3, True2())); +SA(!noexcept(CFalse, True1())); +SA(!noexcept(CFalse, True2())); +SA(!noexcept(CFalse, True3())); +SA(!noexcept(CTrue1, False())); +SA(!noexcept(CTrue2, False())); +SA(!noexcept(CTrue3, False())); Index: testsuite/g++.dg/cpp0x/noexcept01.C === --- testsuite/g++.dg/cpp0x/noexcept01.C (revision 185982) +++ testsuite/g++.dg/cpp0x/noexcept01.C (working copy) @@ -50,7 +50,7 @@ struct E ~E(); }; -SA (!noexcept (E())); +SA (noexcept (E())); struct F { @@ -74,7 +74,7 @@ void tf() } template void tfint,true(); -template void tfE, false(); +template void tfE, true(); // Make sure that noexcept uses the declared exception-specification, not // any knowledge we might have about whether or not the function really Index: testsuite/g++.dg/eh/init-temp1.C
Re: [C++ RFC / Patch] Implementing Deducing noexcept for destructors
... attached the testsuite changes I have so far (seem all rather straightforward to me). Thanks, Paolo. Index: testsuite/g++.old-deja/g++.eh/cleanup1.C === --- testsuite/g++.old-deja/g++.eh/cleanup1.C(revision 185952) +++ testsuite/g++.old-deja/g++.eh/cleanup1.C(working copy) @@ -2,6 +2,12 @@ // Bug: obj gets destroyed twice because the fixups for the return are // inside its cleanup region. +#ifdef __GXX_EXPERIMENTAL_CXX0X__ +#define NOEXCEPT_FALSE noexcept (false) +#else +#define NOEXCEPT_FALSE +#endif + extern C int printf (const char *, ...); int d; @@ -9,7 +15,7 @@ int d; struct myExc { }; struct myExcRaiser { - ~myExcRaiser() { throw myExc(); } + ~myExcRaiser() NOEXCEPT_FALSE { throw myExc(); } }; struct stackObj { Index: testsuite/g++.dg/tree-ssa/ehcleanup-1.C === --- testsuite/g++.dg/tree-ssa/ehcleanup-1.C (revision 185952) +++ testsuite/g++.dg/tree-ssa/ehcleanup-1.C (working copy) @@ -1,9 +1,16 @@ // { dg-options -O2 -fdump-tree-ehcleanup1-details } + +#ifdef __GXX_EXPERIMENTAL_CXX0X__ +#define NOEXCEPT_FALSE noexcept (false) +#else +#define NOEXCEPT_FALSE +#endif + extern void can_throw (); class a { public: - ~a () + ~a () NOEXCEPT_FALSE { if (0) can_throw (); Index: testsuite/g++.dg/cpp0x/noexcept17.C === --- testsuite/g++.dg/cpp0x/noexcept17.C (revision 0) +++ testsuite/g++.dg/cpp0x/noexcept17.C (revision 0) @@ -0,0 +1,54 @@ +// PR c++/50043 +// { dg-options -std=c++11 } + +struct True1 {}; +struct True2 { ~True2(); }; +struct True3 { ~True3(){ throw 0; } }; +struct False { ~False() noexcept(false); }; + +template typename Base +struct A : Base +{ +}; + +template typename Member +struct B +{ +Member mem; +}; + +template typename Base, typename Member +struct C : Base +{ +Member mem; +}; + +#define SA(X) static_assert(X, #X) + +SA( noexcept(True1())); +SA( noexcept(True2())); +SA( noexcept(True3())); +SA(!noexcept(False())); + +SA( noexcept(ATrue1())); +SA( noexcept(ATrue2())); +SA( noexcept(ATrue3())); +SA(!noexcept(AFalse())); + +SA( noexcept(BTrue1())); +SA( noexcept(BTrue2())); +SA( noexcept(BTrue3())); +SA(!noexcept(BFalse())); + +SA( noexcept(CTrue1, True2())); +SA( noexcept(CTrue1, True3())); +SA( noexcept(CTrue2, True3())); +SA( noexcept(CTrue2, True1())); +SA( noexcept(CTrue3, True1())); +SA( noexcept(CTrue3, True2())); +SA(!noexcept(CFalse, True1())); +SA(!noexcept(CFalse, True2())); +SA(!noexcept(CFalse, True3())); +SA(!noexcept(CTrue1, False())); +SA(!noexcept(CTrue2, False())); +SA(!noexcept(CTrue3, False())); Index: testsuite/g++.dg/cpp0x/noexcept01.C === --- testsuite/g++.dg/cpp0x/noexcept01.C (revision 185952) +++ testsuite/g++.dg/cpp0x/noexcept01.C (working copy) @@ -50,7 +50,7 @@ struct E ~E(); }; -SA (!noexcept (E())); +SA (noexcept (E())); struct F { @@ -74,7 +74,7 @@ void tf() } template void tfint,true(); -template void tfE, false(); +template void tfE, true(); // Make sure that noexcept uses the declared exception-specification, not // any knowledge we might have about whether or not the function really Index: testsuite/g++.dg/eh/init-temp1.C === --- testsuite/g++.dg/eh/init-temp1.C(revision 185952) +++ testsuite/g++.dg/eh/init-temp1.C(working copy) @@ -1,6 +1,12 @@ // PR c++/15764 // { dg-do run } +#ifdef __GXX_EXPERIMENTAL_CXX0X__ +#define NOEXCEPT_FALSE noexcept (false) +#else +#define NOEXCEPT_FALSE +#endif + extern C void abort (); int thrown; @@ -8,7 +14,7 @@ int thrown; int as; struct a { a () { ++as; } - ~a () { --as; if (thrown++ == 0) throw 42; } + ~a () NOEXCEPT_FALSE { --as; if (thrown++ == 0) throw 42; } }; int f (a const) { return 1; } Index: testsuite/g++.dg/eh/ctor1.C === --- testsuite/g++.dg/eh/ctor1.C (revision 185952) +++ testsuite/g++.dg/eh/ctor1.C (working copy) @@ -5,6 +5,12 @@ // PR 411 +#ifdef __GXX_EXPERIMENTAL_CXX0X__ +#define NOEXCEPT_FALSE noexcept (false) +#else +#define NOEXCEPT_FALSE +#endif + bool was_f_in_Bar_destroyed=false; struct Foo @@ -17,7 +23,7 @@ struct Foo struct Bar { - ~Bar() + ~Bar() NOEXCEPT_FALSE { throw 1; }
Re: [C++ RFC / Patch] Implementing Deducing noexcept for destructors
On 03/28/2012 06:40 PM, Paolo Carlini wrote: + /* 12.4/3 */ + if (cxx_dialect= cxx0x + DECL_DESTRUCTOR_P (decl) + TYPE_RAISES_EXCEPTIONS (TREE_TYPE (old_decl))) + deduce_noexcept_on_destructor (decl); The exception specification on old_decl doesn't matter; we can drop that test. 2- The new register_specialization bits are needed to cope with (also in the C++ library and elsewhere): That's the wrong place. Why doesn't the code in grokfndecl handle this case? Jason
Re: [C++ RFC / Patch] Implementing Deducing noexcept for destructors
Hi, On 03/28/2012 06:40 PM, Paolo Carlini wrote: + /* 12.4/3 */ + if (cxx_dialect= cxx0x + DECL_DESTRUCTOR_P (decl) + TYPE_RAISES_EXCEPTIONS (TREE_TYPE (old_decl))) +deduce_noexcept_on_destructor (decl); 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)); I don't know if that rings a bell to you... I'll double check anyway. 2- The new register_specialization bits are needed to cope with (also in the C++ library and elsewhere): That's the wrong place. Why doesn't the code in grokfndecl handle this case? Ok, I will check, thanks. Paolo.
Re: [C++ RFC / Patch] Implementing Deducing noexcept for destructors
Hi, On 03/28/2012 06:40 PM, Paolo Carlini wrote: + /* 12.4/3 */ + if (cxx_dialect= cxx0x + DECL_DESTRUCTOR_P (decl) + TYPE_RAISES_EXCEPTIONS (TREE_TYPE (old_decl))) +deduce_noexcept_on_destructor (decl); 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)); I don't know if that rings a bell to you... I'll double check anyway. Yes, If I remove that check, then we hit that gcc_assert for: templatetypename T struct A { ~A(); }; templatetypename T AT::~A() { } Paolo.
Re: [C++ RFC / Patch] Implementing Deducing noexcept for destructors
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. Jason
Re: [C++ RFC / Patch] Implementing Deducing noexcept for destructors
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. Can you suggest a robust way to achieve that? I remained stuck a lot because of this, to make sure that the latter testcase and: templatetypename T struct A { ~A() noexcept; }; templatetypename T AT::~A() { } both work. Thanks, Paolo.
Re: [C++ RFC / Patch] Implementing Deducing noexcept for destructors
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): templatetypename T struct A { ~A(); }; templatetypename T AT::~A() { } we reject, with a different exception specifier error, both: templatetypename T struct A { ~A() noexcept; }; templatetypename T AT::~A() { } and: templatetypename T struct A { ~A(); }; templatetypename T AT::~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. /// 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
Re: [C++ RFC / Patch] Implementing Deducing noexcept for destructors
Hi again, On 03/26/2012 09:31 PM, Jason Merrill wrote: On 03/26/2012 07:22 AM, Paolo Carlini wrote: My basic idea so far is very simple: --- class.c (revision 185792) +++ class.c (working copy) @@ -1001,6 +1001,10 @@ add_method (tree type, tree method, tree using_dec destructor, type); } + else if (cxx_dialect = cxx0x + !TYPE_RAISES_EXCEPTIONS (TREE_TYPE (method))) + TREE_TYPE (method) = build_exception_variant (TREE_TYPE (method), + noexcept_true_spec); } That would implement N1366, but implementing N3204 is a bit more involved. You need to copy TYPE_RAISES_EXCEPTIONS from the result of implicitly_declare_fn; see defaulted_late_check for something similar. Also, this is too early, since we can't know what the eh specification of the implicit declaration would be until the closing brace of the class. I think I understand your explanation and the below appears already to work pretty well. Is it on the right track? What about the check_redeclaration_exception_specification bits? Thanks in advance for any further feedback, Paolo. Index: class.c === --- class.c (revision 185911) +++ class.c (working copy) @@ -4321,6 +4321,37 @@ clone_constructors_and_destructors (tree t) clone_function_decl (OVL_CURRENT (fns), /*update_method_vec_p=*/1); } +/* For each destructor in T, deduce noexcept per: + + 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)) +{ + tree fn = OVL_CURRENT (fns); + if (!TYPE_RAISES_EXCEPTIONS (TREE_TYPE (fn))) + { + tree ctx = DECL_CONTEXT (fn); + tree implicit_fn = implicitly_declare_fn (sfk_destructor, ctx, + /*const_p=*/false); + tree eh_spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (implicit_fn)); + + TREE_TYPE (fn) = build_exception_variant (TREE_TYPE (fn), eh_spec); + } +} +} + /* 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. */ @@ -5129,6 +5160,10 @@ check_bases_and_members (tree t) do anything with non-static member functions. */ finalize_literal_type_property (t); + /* Deduce noexcept. */ + if (cxx_dialect = cxx0x) +deduce_noexcept_on_destructors (t); + /* Create the in-charge and not-in-charge variants of constructors and destructors. */ clone_constructors_and_destructors (t); Index: decl.c === --- decl.c (revision 185911) +++ decl.c (working copy) @@ -1144,7 +1144,13 @@ check_redeclaration_exception_specification (tree if ((pedantic || ! DECL_IN_SYSTEM_HEADER (old_decl)) ! DECL_IS_BUILTIN (old_decl) flag_exceptions - !comp_except_specs (new_exceptions, old_exceptions, ce_normal)) + !comp_except_specs (new_exceptions, old_exceptions, ce_normal) + /* Special case in C++11: noexcept has been deduced as true for +the declaration and there is no exception-specification on the +definition. */ + !(DECL_DESTRUCTOR_P (new_decl) + cxx_dialect = cxx0x + !new_exceptions TYPE_NOEXCEPT_P (old_type))) { error (declaration of %qF has a different exception specifier, new_decl); Index: method.c === --- method.c(revision 185911) +++ 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 185911) +++ cp-tree.h (working copy) @@ -5264,6 +5264,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);
Re: [C++ RFC / Patch] Implementing Deducing noexcept for destructors
On 03/28/2012 11:02 AM, Paolo Carlini wrote: + !comp_except_specs (new_exceptions, old_exceptions, ce_normal) + /* Special case in C++11: noexcept has been deduced as true for +the declaration and there is no exception-specification on the +definition. */ + !(DECL_DESTRUCTOR_P (new_decl) + cxx_dialect= cxx0x + !new_exceptions TYPE_NOEXCEPT_P (old_type))) TYPE_NOEXCEPT_P is the wrong test; the implicit declaration might have an exception-specification that allows some or all exceptions. I think the most straightforward thing would be to add the implicit exception-specification immediately when declaring a destructor outside the class, so that by the time we get to check_redeclaration_exception_specification the EH specs will match. Jason
Re: [C++ RFC / Patch] Implementing Deducing noexcept for destructors
Hi, On 03/28/2012 11:02 AM, Paolo Carlini wrote: + !comp_except_specs (new_exceptions, old_exceptions, ce_normal) + /* Special case in C++11: noexcept has been deduced as true for + the declaration and there is no exception-specification on the + definition. */ + !(DECL_DESTRUCTOR_P (new_decl) + cxx_dialect= cxx0x + !new_exceptions TYPE_NOEXCEPT_P (old_type))) TYPE_NOEXCEPT_P is the wrong test; the implicit declaration might have an exception-specification that allows some or all exceptions. I think the most straightforward thing would be to add the implicit exception-specification immediately when declaring a destructor outside the class, so that by the time we get to check_redeclaration_exception_specification the EH specs will match. Agreed. The below is another iteration (which passes boot test, library included modulo the already mentioned expected failures in C++11 mode), I'm trying to get to the point you by and large like the code proper, thus I can start adjusting the testcases, etc. Anyway, some notes about bits new wrt the previous iterations and not totally obvious given your indications: 1- Turns out the check_bases_and_members change has to happen earlier, because we want to fixup the exceptions before check_bases, otherwise we reject things like (in the C++ library and elsewhere): struct True2 { virtual ~True2() noexcept; }; template typename Base struct C : Base { ~C(); }; 2- The new register_specialization bits are needed to cope with (also in the C++ library and elsewhere): templatetypename T struct A { ~A(); }; template Aint::~A(); template Aint::~A() { } As a matter of fact, though, there is one more path in register_specialization which leads to a duplicate_decls call, I'm not 100% sure we can leave it alone. 3- Names of the new functions, files to which belong, I'm just guessing. Thanks, Paolo. / Index: class.c === --- class.c (revision 185920) +++ class.c (working copy) @@ -4321,6 +4321,40 @@ 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 +5028,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 185920) +++ decl.c (working copy) @@ -7528,6 +7528,12 @@ grokfndecl (tree ctype, if (TREE_CODE (decl) == TEMPLATE_DECL) decl = DECL_TEMPLATE_RESULT (decl); + /* 12.4/3 */ + if (cxx_dialect = cxx0x + DECL_DESTRUCTOR_P (decl) + TYPE_RAISES_EXCEPTIONS (TREE_TYPE (old_decl))) + deduce_noexcept_on_destructor (decl); + /* Attempt to merge the declarations. This can fail, in the case of some invalid specialization declarations. */ pushed_scope = push_scope (ctype); Index: method.c === --- method.c(revision 185920) +++ 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: pt.c
Re: [C++ RFC / Patch] Implementing Deducing noexcept for destructors
Oops... 1- Turns out the check_bases_and_members change has to happen earlier, because we want to fixup the exceptions before check_bases, otherwise we reject things like (in the C++ library and elsewhere): struct True2 { virtual ~True2() noexcept; }; template typename Base struct C : Base { ~C(); }; Last line of the snippet missing: CTrue2 c; Paolo.
[C++ RFC / Patch] Implementing Deducing noexcept for destructors
[sorry, I'm resending this because inadvertently I had some html and the message got rejected] Hi, thus, I have been working on c++/50043, which boils down to this: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3204.htm My basic idea so far is very simple: --- class.c (revision 185792) +++ class.c (working copy) @@ -1001,6 +1001,10 @@ add_method (tree type, tree method, tree using_dec destructor, type); } + else if (cxx_dialect = cxx0x + !TYPE_RAISES_EXCEPTIONS (TREE_TYPE (method))) + TREE_TYPE (method) = build_exception_variant (TREE_TYPE (method), + noexcept_true_spec); } else { thus, right before actually adding the method we check whether nothing has been deduced about it and we enforce the default noexcept. It's already enough to cover the testcase provided in c++/50043, which includes a good range of positive and negative tests. Does the idea make sense? As is, the patchlet leads to a few regressions, largely benign as far as I can see: FAIL: g++.dg/cpp0x/noexcept01.C (test for excess errors) FAIL: g++.dg/eh/ctor1.C -std=c++11 execution test FAIL: g++.dg/eh/init-temp1.C -std=c++11 execution test FAIL: g++.dg/tree-ssa/ehcleanup-1.C -std=gnu++11 scan-tree-dump-times ehcleanup1 Removing unreachable 4 FAIL: g++.old-deja/g++.eh/cleanup1.C -std=c++11 execution test (the idea would changing the tests to be c++98 only and then adding c++11 counterparts) Something the patchlet does not cover is: struct B { ~B(); }; B::~B() { } mailto:paolo.carl...@oracle.com indeed the as an implicit declaration bits of the new wording in C++11 doesn't guide so much about this, I guess it means something like: --- decl.c (revision 185792) +++ decl.c (working copy) @@ -1144,7 +1144,10 @@ check_redeclaration_exception_specification (tree if ((pedantic || ! DECL_IN_SYSTEM_HEADER (old_decl)) ! DECL_IS_BUILTIN (old_decl) flag_exceptions - !comp_except_specs (new_exceptions, old_exceptions, ce_normal)) + !comp_except_specs (new_exceptions, old_exceptions, ce_normal) + !(DECL_DESTRUCTOR_P (new_decl) + cxx_dialect = cxx0x + !new_exceptions TYPE_NOEXCEPT_P (old_type))) { error (declaration of %qF has a different exception specifier, new_decl); does it make sense? Another case which makes me nervous is when we add to the testcase in c++/50043 also a case for a virtual base class destructor, thus something like struct True2 { virtual ~True2(); }; struct False { ~False() noexcept(false); }; template typename Base, typename Member struct C : Base { Member mem; }; SA(!noexcept(CTrue2, False())); it doesn't compile at all because: noexcept_PR50043.C:21:8: error: looser throw specifier for ‘virtual CTrue2, False::~C() noexcept (false)’ noexcept_PR50043.C:5:24: error: overriding ‘virtual True2::~True2() noexcept (true)’ is this expected? Maybe, but I'm not sure. Thanks in advance for any tips! Paolo.
Re: [C++ RFC / Patch] Implementing Deducing noexcept for destructors
On 03/26/2012 07:22 AM, Paolo Carlini wrote: My basic idea so far is very simple: --- class.c (revision 185792) +++ class.c (working copy) @@ -1001,6 +1001,10 @@ add_method (tree type, tree method, tree using_dec destructor, type); } + else if (cxx_dialect = cxx0x + !TYPE_RAISES_EXCEPTIONS (TREE_TYPE (method))) + TREE_TYPE (method) = build_exception_variant (TREE_TYPE (method), + noexcept_true_spec); } That would implement N1366, but implementing N3204 is a bit more involved. You need to copy TYPE_RAISES_EXCEPTIONS from the result of implicitly_declare_fn; see defaulted_late_check for something similar. Also, this is too early, since we can't know what the eh specification of the implicit declaration would be until the closing brace of the class. struct True2 { virtual ~True2(); }; struct False { ~False() noexcept(false); }; template typename Base, typename Member struct C : Base { Member mem; }; SA(!noexcept(CTrue2, False())); it doesn't compile at all because: noexcept_PR50043.C:21:8: error: looser throw specifier for ‘virtual CTrue2, False::~C() noexcept (false)’ noexcept_PR50043.C:5:24: error: overriding ‘virtual True2::~True2() noexcept (true)’ is this expected? Maybe, but I'm not sure. Yes. Adding noexcept to ~True2 causes the same error without your patch. Jason
Re: [C++ RFC / Patch] Implementing Deducing noexcept for destructors
On 03/26/2012 09:31 PM, Jason Merrill wrote: On 03/26/2012 07:22 AM, Paolo Carlini wrote: My basic idea so far is very simple: --- class.c (revision 185792) +++ class.c (working copy) @@ -1001,6 +1001,10 @@ add_method (tree type, tree method, tree using_dec destructor, type); } + else if (cxx_dialect = cxx0x + !TYPE_RAISES_EXCEPTIONS (TREE_TYPE (method))) + TREE_TYPE (method) = build_exception_variant (TREE_TYPE (method), + noexcept_true_spec); } That would implement N1366, but implementing N3204 is a bit more involved. You need to copy TYPE_RAISES_EXCEPTIONS from the result of implicitly_declare_fn; see defaulted_late_check for something similar. Also, this is too early, since we can't know what the eh specification of the implicit declaration would be until the closing brace of the class. Thanks for the help Jason. Paolo.