]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
re PR c++/26714 (violation of [class.temporary]/5)
authorJason Merrill <jason@redhat.com>
Sat, 5 Nov 2011 03:28:14 +0000 (23:28 -0400)
committerJason Merrill <jason@gcc.gnu.org>
Sat, 5 Nov 2011 03:28:14 +0000 (23:28 -0400)
PR c++/26714
* init.c (perform_member_init): Strip TARGET_EXPR around NSDMI.
Do temporary lifetime extension.

From-SVN: r181002

gcc/cp/ChangeLog
gcc/cp/init.c
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/cpp0x/initlist-lifetime2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/init/lifetime2.C [new file with mode: 0644]

index 6c68d8798da02c6ddaa2422ba890a4070d9f830c..200621e3f9f9f4b3d41fc6163f09a49f8d0c31e6 100644 (file)
@@ -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.
 
index 38812754641e071867d69e21c2464afb51d16f67..ca4f590cc062181636cd6bf0cf3f852c9dc55c27 100644 (file)
@@ -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))))
     {
index 2e05a080ccb6f44d9bd59da62f890b5b6bfa8c37..a6e95c5a65d975d857afdf110b5a901d2910f452 100644 (file)
@@ -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 (file)
index 0000000..16ae1ac
--- /dev/null
@@ -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 (file)
index 0000000..293bd69
--- /dev/null
@@ -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;
+}