aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason Merrill <jason@redhat.com>2011-11-04 23:28:14 -0400
committerJason Merrill <jason@gcc.gnu.org>2011-11-04 23:28:14 -0400
commite2df21bfc6c81b4bc410a42002c8427454ffa628 (patch)
tree7b439ecb9236581bd4b64ccf943c6d9a7ce82aa0
parent8dc1dc7975a3d8dd3ffdfd6fcc992419cc358af7 (diff)
downloadgcc-e2df21bfc6c81b4bc410a42002c8427454ffa628.zip
gcc-e2df21bfc6c81b4bc410a42002c8427454ffa628.tar.gz
gcc-e2df21bfc6c81b4bc410a42002c8427454ffa628.tar.bz2
re PR c++/26714 (violation of [class.temporary]/5)
PR c++/26714 * init.c (perform_member_init): Strip TARGET_EXPR around NSDMI. Do temporary lifetime extension. From-SVN: r181002
-rw-r--r--gcc/cp/ChangeLog4
-rw-r--r--gcc/cp/init.c46
-rw-r--r--gcc/testsuite/ChangeLog4
-rw-r--r--gcc/testsuite/g++.dg/cpp0x/initlist-lifetime2.C64
-rw-r--r--gcc/testsuite/g++.dg/init/lifetime2.C23
5 files changed, 140 insertions, 1 deletions
diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index 6c68d87..200621e 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -1,5 +1,9 @@
2011-11-04 Jason Merrill <jason@redhat.com>
+ PR c++/26714
+ * init.c (perform_member_init): Strip TARGET_EXPR around NSDMI.
+ Do temporary lifetime extension.
+
PR c++/48370
* decl.c (cp_finish_decl): Run cleanups in the right order.
diff --git a/gcc/cp/init.c b/gcc/cp/init.c
index 3881275..ca4f590 100644
--- a/gcc/cp/init.c
+++ b/gcc/cp/init.c
@@ -507,7 +507,15 @@ perform_member_init (tree member, tree init)
tf_warning_or_error, member, /*function_p=*/false,
/*integral_constant_expression_p=*/false));
else
- init = break_out_target_exprs (DECL_INITIAL (member));
+ {
+ init = DECL_INITIAL (member);
+ /* Strip redundant TARGET_EXPR so we don't need to remap it, and
+ so the aggregate init code below will see a CONSTRUCTOR. */
+ if (init && TREE_CODE (init) == TARGET_EXPR
+ && !VOID_TYPE_P (TREE_TYPE (TARGET_EXPR_INITIAL (init))))
+ init = TARGET_EXPR_INITIAL (init);
+ init = break_out_target_exprs (init);
+ }
}
/* Effective C++ rule 12 requires that all data members be
@@ -565,6 +573,42 @@ perform_member_init (tree member, tree init)
finish_expr_stmt (init);
}
}
+ else if (init
+ && (TREE_CODE (type) == REFERENCE_TYPE
+ /* Pre-digested NSDMI. */
+ || (((TREE_CODE (init) == CONSTRUCTOR
+ && TREE_TYPE (init) == type)
+ /* { } mem-initializer. */
+ || (TREE_CODE (init) == TREE_LIST
+ && TREE_CODE (TREE_VALUE (init)) == CONSTRUCTOR
+ && CONSTRUCTOR_IS_DIRECT_INIT (TREE_VALUE (init))))
+ && (CP_AGGREGATE_TYPE_P (type)
+ || is_std_init_list (type)))))
+ {
+ /* With references and list-initialization, we need to deal with
+ extending temporary lifetimes. 12.2p5: "A temporary bound to a
+ reference member in a constructor’s ctor-initializer (12.6.2)
+ persists until the constructor exits." */
+ unsigned i; tree t;
+ VEC(tree,gc) *cleanups = make_tree_vector ();
+ if (TREE_CODE (init) == TREE_LIST)
+ init = build_x_compound_expr_from_list (init, ELK_MEM_INIT,
+ tf_warning_or_error);
+ if (TREE_TYPE (init) != type)
+ init = digest_init (type, init, tf_warning_or_error);
+ if (init == error_mark_node)
+ return;
+ /* Use 'this' as the decl, as it has the lifetime we want. */
+ init = extend_ref_init_temps (current_class_ptr, init, &cleanups);
+ if (TREE_CODE (type) == ARRAY_TYPE
+ && TYPE_HAS_NONTRIVIAL_DESTRUCTOR (TREE_TYPE (type)))
+ init = build_vec_init_expr (type, init, tf_warning_or_error);
+ init = build2 (INIT_EXPR, type, decl, init);
+ finish_expr_stmt (init);
+ FOR_EACH_VEC_ELT (tree, cleanups, i, t)
+ push_cleanup (decl, t, false);
+ release_tree_vector (cleanups);
+ }
else if (type_build_ctor_call (type)
|| (init && CLASS_TYPE_P (strip_array_types (type))))
{
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index 2e05a08..a6e95c5 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,5 +1,9 @@
2011-11-04 Jason Merrill <jason@redhat.com>
+ PR c++/26714
+ * g++.dg/init/lifetime2.C: New.
+ * g++.dg/cpp0x/initlist-lifetime2.C: New.
+
PR c++/48370
* g++.dg/init/lifetime1.C: Test cleanup order.
diff --git a/gcc/testsuite/g++.dg/cpp0x/initlist-lifetime2.C b/gcc/testsuite/g++.dg/cpp0x/initlist-lifetime2.C
new file mode 100644
index 0000000..16ae1ac
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/initlist-lifetime2.C
@@ -0,0 +1,64 @@
+// Test that we properly extend the lifetime of the initializer_list
+// array even if the initializer_list is a subobject.
+// { dg-options -std=c++0x }
+// { dg-do run }
+
+#include <initializer_list>
+
+extern "C" void abort();
+bool ok;
+
+bool do_throw;
+
+struct A {
+ A(int) { if (do_throw) throw 42; }
+ ~A() { if (!ok) abort(); }
+};
+
+typedef std::initializer_list<A> AL;
+typedef std::initializer_list<AL> AL2;
+typedef std::initializer_list<AL2> AL3;
+
+struct B {
+ AL al;
+ const AL& alr;
+};
+
+struct A2
+{
+ const A& a1;
+ const A& a2;
+};
+
+struct C {
+ AL ar[2];
+ B b;
+ AL3 al3;
+ A2 a2;
+ A2 a2r[2];
+ C():
+ ar{{1,2},{3,4}},
+ b{{5,6},{7,8}},
+ al3{{{1},{2},{3}}},
+ a2{1,2},
+ a2r{{1,2},{3,4}}
+ { ok = true; }
+};
+
+struct D {
+ AL ar[2] = {{1,2},{3,4}};
+ B b = {{5,6},{7,8}};
+ AL3 al3 = {{{1},{2},{3}}};
+ A2 a2 = {1,2};
+ A2 a2r[2] = {{1,2},{3,4}};
+ D() { ok = true; }
+};
+
+int main(int argc, const char** argv)
+{
+ do_throw = (argc > 1); // always false, but optimizer can't tell
+ ok = false;
+ C c;
+ ok = false;
+ D d;
+}
diff --git a/gcc/testsuite/g++.dg/init/lifetime2.C b/gcc/testsuite/g++.dg/init/lifetime2.C
new file mode 100644
index 0000000..293bd69
--- /dev/null
+++ b/gcc/testsuite/g++.dg/init/lifetime2.C
@@ -0,0 +1,23 @@
+// PR c++/26714
+// { dg-do run }
+
+extern "C" void abort();
+
+bool ok = false;
+struct A
+{
+ A() { }
+ ~A() { if (!ok) abort(); }
+};
+
+struct B
+{
+ const A &a1;
+ const A &a2;
+ B() : a1(A()),a2(A()) { ok = true; }
+};
+
+int main()
+{
+ B b;
+}