This PR has been on my mind for quite some time but I've not found a solution
that I'd like. Maybe one of you can think of something better.
The problem in this test is that in C++11, .eh optimizes out the catch,
so the exception is never caught. That is because lower_catch doesn't
think that the region may throw (eh_region_may_contain_throw). That's
so because P::P () is marked as TREE_NOTHROW, which is wrong, because
it calls X::X() which calls init() with throw. TREE_NOTHROW is set in
finish_function:
/* If this function can't throw any exceptions, remember that. */
if (!processing_template_decl
&& !cp_function_chain->can_throw
&& !flag_non_call_exceptions
&& !decl_replaceable_p (fndecl))
TREE_NOTHROW (fndecl) = 1;
P::P() should have been marked as can_throw in set_flags_from_callee, but when
processing X::X() cfun is null, so we can't set it. P::P() is created only
later via implicitly_declare_fn. So one way to fix it would be to remember
that the class has a subobject whose constructor may throw, so the class's
constructor can throw, too. I added a test with more nested classes, too.
I dislike adding a new flag for this scenario; anybody see a better way to
approach this?
Bootstrapped/regtested on x86_64-linux.
2018-05-16 Marek Polacek <[email protected]>
PR c++/85363
* cp-tree.h (struct lang_type): Add ctor_may_throw.
(CLASSTYPE_CTOR_MAY_THROW): New.
* call.c (set_flags_from_callee): Set it.
* decl.c (finish_function): Check it.
* g++.dg/cpp0x/initlist-throw1.C: New test.
* g++.dg/cpp0x/initlist-throw2.C: New test.
diff --git gcc/cp/call.c gcc/cp/call.c
index 09a3618b007..f839d943443 100644
--- gcc/cp/call.c
+++ gcc/cp/call.c
@@ -332,6 +332,8 @@ set_flags_from_callee (tree call)
if (!nothrow && at_function_scope_p () && cfun && cp_function_chain)
cp_function_chain->can_throw = 1;
+ else if (!nothrow && at_class_scope_p () && decl && DECL_CONSTRUCTOR_P
(decl))
+ CLASSTYPE_CTOR_MAY_THROW (current_class_type) = true;
if (decl && TREE_THIS_VOLATILE (decl) && cfun && cp_function_chain)
current_function_returns_abnormally = 1;
diff --git gcc/cp/cp-tree.h gcc/cp/cp-tree.h
index cab926028b8..7a781a9a8a3 100644
--- gcc/cp/cp-tree.h
+++ gcc/cp/cp-tree.h
@@ -2025,6 +2025,7 @@ struct GTY(()) lang_type {
unsigned has_constexpr_ctor : 1;
unsigned unique_obj_representations : 1;
unsigned unique_obj_representations_set : 1;
+ unsigned ctor_may_throw : 1;
/* When adding a flag here, consider whether or not it ought to
apply to a template instance if it applies to the template. If
@@ -2033,7 +2034,7 @@ struct GTY(()) lang_type {
/* There are some bits left to fill out a 32-bit word. Keep track
of this by updating the size of this bitfield whenever you add or
remove a flag. */
- unsigned dummy : 4;
+ unsigned dummy : 3;
tree primary_base;
vec<tree_pair_s, va_gc> *vcall_indices;
@@ -2105,6 +2106,10 @@ struct GTY(()) lang_type {
#define CLASSTYPE_LAZY_DESTRUCTOR(NODE) \
(LANG_TYPE_CLASS_CHECK (NODE)->lazy_destructor)
+/* Nonzero means that NODE (a class type) has a constructor that can throw. */
+#define CLASSTYPE_CTOR_MAY_THROW(NODE) \
+ (LANG_TYPE_CLASS_CHECK (NODE)->ctor_may_throw)
+
/* Nonzero means that NODE (a class type) is final */
#define CLASSTYPE_FINAL(NODE) \
TYPE_FINAL_P (NODE)
diff --git gcc/cp/decl.c gcc/cp/decl.c
index 10e3079beed..c5799459210 100644
--- gcc/cp/decl.c
+++ gcc/cp/decl.c
@@ -15651,7 +15651,11 @@ finish_function (bool inline_p)
if (!processing_template_decl
&& !cp_function_chain->can_throw
&& !flag_non_call_exceptions
- && !decl_replaceable_p (fndecl))
+ && !decl_replaceable_p (fndecl)
+ /* If FNDECL is a constructor of a class that can call a throwing
+ constructor, don't mark it as non-throwing. */
+ && (!DECL_CONSTRUCTOR_P (fndecl)
+ || !CLASSTYPE_CTOR_MAY_THROW (DECL_CONTEXT (fndecl))))
TREE_NOTHROW (fndecl) = 1;
/* This must come after expand_function_end because cleanups might
diff --git gcc/testsuite/g++.dg/cpp0x/initlist-throw1.C
gcc/testsuite/g++.dg/cpp0x/initlist-throw1.C
index e69de29bb2d..264c6c7a7a0 100644
--- gcc/testsuite/g++.dg/cpp0x/initlist-throw1.C
+++ gcc/testsuite/g++.dg/cpp0x/initlist-throw1.C
@@ -0,0 +1,29 @@
+// PR c++/85363
+// { dg-do run { target c++11 } }
+
+int
+init (int f)
+{
+ throw f;
+}
+
+struct X {
+ X (int f) : n {init (f)} {}
+ int n;
+};
+
+struct P {
+ X x{20};
+};
+
+int
+main ()
+{
+ try {
+ P p {};
+ }
+ catch (int n) {
+ return 0;
+ }
+ return 1;
+}
diff --git gcc/testsuite/g++.dg/cpp0x/initlist-throw2.C
gcc/testsuite/g++.dg/cpp0x/initlist-throw2.C
index e69de29bb2d..24906374fa4 100644
--- gcc/testsuite/g++.dg/cpp0x/initlist-throw2.C
+++ gcc/testsuite/g++.dg/cpp0x/initlist-throw2.C
@@ -0,0 +1,33 @@
+// PR c++/85363
+// { dg-do run { target c++11 } }
+
+int
+init (int f)
+{
+ throw f;
+}
+
+struct X {
+ X () : n {init (42)} {}
+ int n;
+};
+
+struct P {
+ struct R {
+ struct Q {
+ X x = {};
+ } q;
+ } r;
+};
+
+int
+main ()
+{
+ try {
+ P p {};
+ }
+ catch (int n) {
+ return 0;
+ }
+ return 1;
+}