Tested x86_64-pc-linux-gnu, applying to trunk.

-- 8< --

A later testcase in PR118856 highlights a preexisting problem with multiple
reference-extended temporaries in a single declaration; if initializing a
later one throws, the cleanup for the earlier one is not in scope yet.
Let's deal with this by keeping a dummy TARGET_EXPR to hold the EH cleanup
until all other initialization is complete.  See the comment for various
other considered approaches.

We now avoid extending TARGET_EXPRs with CLEANUP_EH_ONLY set; all such
TARGET_EXPRs were already only internal iterator/flag variables that don't
want to be extended, as they are dead after initialization is complete even
if other temporaries are extended.  But some other internal temporaries did
not have the flag set because they don't have TARGET_EXPR_CLEANUP; I
introduce a get_internal_target_expr function to set the flag rather than
directly set the flag (and add a comment) in such places.  The places
changed to call get_internal_target_expr either already set the flag, or
have no cleanup at all.

        PR c++/118856

gcc/cp/ChangeLog:

        * call.cc (set_up_extended_ref_temp): Retain a TARGET_EXPR for
        cleanups if something later in initialization throws.
        (extend_temps_r): Don't extend eliding or EH-only TARGET_EXPRs.
        * cp-tree.h (get_internal_target_expr): Declare.
        * tree.cc (get_internal_target_expr): New.
        * decl.cc (cp_finish_decomp, expand_static_init): Use it.
        * except.cc (build_throw): Likewise.
        * init.cc (build_new_1, build_vec_init, build_delete): Likewise.
        (build_vec_delete): Likewise.
        * typeck2.cc (maybe_push_temp_cleanup): Likewise.

gcc/testsuite/ChangeLog:

        * g++.dg/eh/ref-temp3.C: New test.
        * g++.dg/eh/ref-temp4.C: New test.
---
 gcc/cp/cp-tree.h                    |  1 +
 gcc/cp/call.cc                      | 40 ++++++++++++++++++++++++++++-
 gcc/cp/decl.cc                      |  4 +--
 gcc/cp/except.cc                    |  2 +-
 gcc/cp/init.cc                      | 16 ++++++------
 gcc/cp/tree.cc                      | 21 +++++++++++++++
 gcc/cp/typeck2.cc                   |  2 +-
 gcc/testsuite/g++.dg/eh/ref-temp3.C | 36 ++++++++++++++++++++++++++
 gcc/testsuite/g++.dg/eh/ref-temp4.C | 35 +++++++++++++++++++++++++
 9 files changed, 144 insertions(+), 13 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/eh/ref-temp3.C
 create mode 100644 gcc/testsuite/g++.dg/eh/ref-temp4.C

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index b7749eb2b32..d87cc1555b7 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -8207,6 +8207,7 @@ extern bool is_local_temp                 (tree);
 extern tree build_aggr_init_expr               (tree, tree);
 extern tree get_target_expr                    (tree,
                                                 tsubst_flags_t = 
tf_warning_or_error);
+extern tree get_internal_target_expr           (tree);
 extern tree build_cplus_array_type             (tree, tree, int is_dep = -1);
 extern tree build_array_of_n_type              (tree, unsigned HOST_WIDE_INT);
 extern bool array_of_runtime_bound_p           (tree);
diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc
index 38a8f7fdcda..03130f80f86 100644
--- a/gcc/cp/call.cc
+++ b/gcc/cp/call.cc
@@ -14251,6 +14251,8 @@ set_up_extended_ref_temp (tree decl, tree expr, 
vec<tree, va_gc> **cleanups,
        init = add_stmt_to_compound (init, register_dtor_fn (var));
       else
        {
+         /* ??? Instead of rebuilding the cleanup, we could replace the slot
+            with var in TARGET_EXPR_CLEANUP (expr).  */
          tree cleanup = cxx_maybe_build_cleanup (var, tf_warning_or_error);
          if (cleanup)
            {
@@ -14269,6 +14271,37 @@ set_up_extended_ref_temp (tree decl, tree expr, 
vec<tree, va_gc> **cleanups,
                  cleanup = build3 (COND_EXPR, void_type_node,
                                    *cond_guard, cleanup, NULL_TREE);
                }
+             if (flag_exceptions && TREE_CODE (TREE_TYPE (var)) != ARRAY_TYPE)
+               {
+                 /* The normal cleanup for this extended variable isn't pushed
+                    until cp_finish_decl, so we need to retain a TARGET_EXPR
+                    to clean it up in case a later initializer throws
+                    (g++.dg/eh/ref-temp3.C).
+
+                    We don't do this for array temporaries because they have
+                    the array cleanup region from build_vec_init.
+
+                    Unlike maybe_push_temp_cleanup, we don't actually need a
+                    flag, but a TARGET_EXPR needs a TARGET_EXPR_SLOT.
+                    Perhaps this could use WITH_CLEANUP_EXPR instead, but
+                    gimplify.cc doesn't handle that, and front-end handling
+                    was removed in r8-1725 and r8-1818.
+
+                    Alternately it might be preferable to flatten an
+                    initialization with extended temps into a sequence of
+                    (non-full-expression) statements, so we could immediately
+                    push_cleanup here for only a single cleanup region, but we
+                    don't have a mechanism for that in the front-end, only the
+                    gimplifier.  */
+                 tree targ = get_internal_target_expr (boolean_true_node);
+                 TARGET_EXPR_CLEANUP (targ) = cleanup;
+                 CLEANUP_EH_ONLY (targ) = true;
+                 /* Don't actually initialize the bool.  */
+                 init = (!init ? void_node
+                         : convert_to_void (init, ICV_STATEMENT, tf_none));
+                 TARGET_EXPR_INITIAL (targ) = init;
+                 init = targ;
+               }
              vec_safe_push (*cleanups, cleanup);
            }
        }
@@ -14886,7 +14919,12 @@ extend_temps_r (tree *tp, int *walk_subtrees, void 
*data)
         TREE_CODE (*p) == COMPONENT_REF || TREE_CODE (*p) == ARRAY_REF; )
       p = &TREE_OPERAND (*p, 0);
 
-  if (TREE_CODE (*p) == TARGET_EXPR)
+  if (TREE_CODE (*p) == TARGET_EXPR
+      /* An eliding TARGET_EXPR isn't a temporary at all.  */
+      && !TARGET_EXPR_ELIDING_P (*p)
+      /* A TARGET_EXPR with CLEANUP_EH_ONLY is an artificial variable used
+        during initialization, and need not be extended.  */
+      && !CLEANUP_EH_ONLY (*p))
     {
       tree subinit = NULL_TREE;
       tree slot = TARGET_EXPR_SLOT (*p);
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index df4e66798b1..05ad9bb24d5 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -10024,7 +10024,7 @@ cp_finish_decomp (tree decl, cp_decomp *decomp, bool 
test_p)
              /* Wrap that value into a TARGET_EXPR, emit it right
                 away and save for later uses in the cp_parse_condition
                 or its instantiation.  */
-             cond = get_target_expr (cond);
+             cond = get_internal_target_expr (cond);
              add_stmt (cond);
              DECL_DECOMP_BASE (decl) = cond;
            }
@@ -10687,7 +10687,7 @@ expand_static_init (tree decl, tree init)
                               inner_if_stmt);
 
          inner_then_clause = begin_compound_stmt (BCS_NO_SCOPE);
-         begin = get_target_expr (boolean_false_node);
+         begin = get_internal_target_expr (boolean_false_node);
          flag = TARGET_EXPR_SLOT (begin);
 
          TARGET_EXPR_CLEANUP (begin)
diff --git a/gcc/cp/except.cc b/gcc/cp/except.cc
index 1c1894b092d..a9d8e2ffb57 100644
--- a/gcc/cp/except.cc
+++ b/gcc/cp/except.cc
@@ -712,7 +712,7 @@ build_throw (location_t loc, tree exp, tsubst_flags_t 
complain)
       allocate_expr = do_allocate_exception (temp_type);
       if (allocate_expr == error_mark_node)
        return error_mark_node;
-      allocate_expr = get_target_expr (allocate_expr);
+      allocate_expr = get_internal_target_expr (allocate_expr);
       ptr = TARGET_EXPR_SLOT (allocate_expr);
       TARGET_EXPR_CLEANUP (allocate_expr) = do_free_exception (ptr);
       CLEANUP_EH_ONLY (allocate_expr) = 1;
diff --git a/gcc/cp/init.cc b/gcc/cp/init.cc
index bc075473554..f77da4267cb 100644
--- a/gcc/cp/init.cc
+++ b/gcc/cp/init.cc
@@ -3529,7 +3529,7 @@ build_new_1 (vec<tree, va_gc> **placement, tree type, 
tree nelts,
          && (INTEGRAL_OR_ENUMERATION_TYPE_P (TREE_TYPE (TREE_TYPE (placement)))
              || VOID_TYPE_P (TREE_TYPE (TREE_TYPE (placement)))))
        {
-         placement_expr = get_target_expr (placement_first);
+         placement_expr = get_internal_target_expr (placement_first);
          CALL_EXPR_ARG (alloc_call, 1)
            = fold_convert (TREE_TYPE (placement), placement_expr);
        }
@@ -3557,7 +3557,7 @@ build_new_1 (vec<tree, va_gc> **placement, tree type, 
tree nelts,
 
   /* Store the result of the allocation call in a variable so that we can
      use it more than once.  */
-  alloc_expr = get_target_expr (alloc_expr);
+  alloc_expr = get_internal_target_expr (alloc_expr);
   alloc_node = TARGET_EXPR_SLOT (alloc_expr);
 
   /* Strip any COMPOUND_EXPRs from ALLOC_CALL.  */
@@ -3841,7 +3841,7 @@ build_new_1 (vec<tree, va_gc> **placement, tree type, 
tree nelts,
        {
          tree end, sentry, begin;
 
-         begin = get_target_expr (boolean_true_node);
+         begin = get_internal_target_expr (boolean_true_node);
 
          sentry = TARGET_EXPR_SLOT (begin);
 
@@ -4679,7 +4679,7 @@ build_vec_init (tree base, tree maxindex, tree init,
   current_stmt_tree ()->stmts_are_full_exprs_p = 0;
   rval = get_temp_regvar (ptype, base);
   base = get_temp_regvar (ptype, rval);
-  tree iterator_targ = get_target_expr (maxindex);
+  tree iterator_targ = get_internal_target_expr (maxindex);
   add_stmt (iterator_targ);
   iterator = TARGET_EXPR_SLOT (iterator_targ);
 
@@ -5323,7 +5323,7 @@ build_delete (location_t loc, tree otype, tree addr,
   if (deleting)
     /* We will use ADDR multiple times so we must save it.  */
     {
-      addr_expr = get_target_expr (addr);
+      addr_expr = get_internal_target_expr (addr);
       addr = TARGET_EXPR_SLOT (addr_expr);
     }
 
@@ -5348,7 +5348,7 @@ build_delete (location_t loc, tree otype, tree addr,
      delete'.  */
   else if (use_global_delete)
     {
-      head = get_target_expr (build_headof (addr));
+      head = get_internal_target_expr (build_headof (addr));
       /* Delete the object.  */
       do_delete = build_op_delete_call (DELETE_EXPR,
                                        head,
@@ -5582,7 +5582,7 @@ build_vec_delete (location_t loc, tree base, tree 
maxindex,
       base = mark_rvalue_use (base);
       if (TREE_SIDE_EFFECTS (base))
        {
-         base_init = get_target_expr (base);
+         base_init = get_internal_target_expr (base);
          base = TARGET_EXPR_SLOT (base_init);
        }
       type = strip_array_types (TREE_TYPE (type));
@@ -5603,7 +5603,7 @@ build_vec_delete (location_t loc, tree base, tree 
maxindex,
        return error_mark_node;
       if (TREE_SIDE_EFFECTS (base))
        {
-         base_init = get_target_expr (base);
+         base_init = get_internal_target_expr (base);
          base = TARGET_EXPR_SLOT (base_init);
        }
     }
diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc
index bf84fb6bcec..611930b3c28 100644
--- a/gcc/cp/tree.cc
+++ b/gcc/cp/tree.cc
@@ -969,6 +969,27 @@ get_target_expr (tree init, tsubst_flags_t complain /* = 
tf_warning_or_error */)
     }
 }
 
+/* Like get_target_expr, but for an internal detail like a cleanup flag or loop
+   iterator.  These variables should not be extended by extend_all_temps.
+
+   This function can also be used for an ephemeral copy of a scalar value such
+   as the pointer to the allocated memory in build_new_1.
+
+   This function should not be used for objects that are part of the abstract
+   C++ semantics such as in stabilize_expr.  */
+
+tree
+get_internal_target_expr (tree init)
+{
+  init = convert_bitfield_to_declared_type (init);
+  tree t = build_target_expr_with_type (init, TREE_TYPE (init),
+                                       tf_warning_or_error);
+  /* No internal variable should have a cleanup on the normal path, and
+     extend_temps_r checks this flag to decide whether to extend.  */
+  CLEANUP_EH_ONLY (t) = true;
+  return t;
+}
+
 /* If EXPR is a bitfield reference, convert it to the declared type of
    the bitfield, and return the resulting expression.  Otherwise,
    return EXPR itself.  */
diff --git a/gcc/cp/typeck2.cc b/gcc/cp/typeck2.cc
index e88f6f0ad51..2555e9c1b64 100644
--- a/gcc/cp/typeck2.cc
+++ b/gcc/cp/typeck2.cc
@@ -457,7 +457,7 @@ maybe_push_temp_cleanup (tree sub, vec<tree,va_gc> **flags)
   if (tree cleanup
       = cxx_maybe_build_cleanup (sub, tf_warning_or_error))
     {
-      tree tx = get_target_expr (boolean_true_node);
+      tree tx = get_internal_target_expr (boolean_true_node);
       tree flag = TARGET_EXPR_SLOT (tx);
       CLEANUP_EH_ONLY (tx) = true;
       TARGET_EXPR_CLEANUP (tx) = build3 (COND_EXPR, void_type_node,
diff --git a/gcc/testsuite/g++.dg/eh/ref-temp3.C 
b/gcc/testsuite/g++.dg/eh/ref-temp3.C
new file mode 100644
index 00000000000..44a729dc2ec
--- /dev/null
+++ b/gcc/testsuite/g++.dg/eh/ref-temp3.C
@@ -0,0 +1,36 @@
+// { dg-do run }
+
+extern "C" void abort();
+
+int a;
+
+struct A {
+  A()
+  {
+    if (a == 1)
+      throw 42;
+    ++a;
+  }
+  ~A()
+  {
+    --a;
+  }
+};
+
+struct B {
+  const A &a1;
+  const A &a2;
+};
+
+void f();
+
+int main()
+{
+  try
+    {
+      B b = { A(), A() };
+    }
+  catch (...) { }
+  if (a != 0)
+    abort ();
+}
diff --git a/gcc/testsuite/g++.dg/eh/ref-temp4.C 
b/gcc/testsuite/g++.dg/eh/ref-temp4.C
new file mode 100644
index 00000000000..bef31cd4346
--- /dev/null
+++ b/gcc/testsuite/g++.dg/eh/ref-temp4.C
@@ -0,0 +1,35 @@
+// PR c++/118856
+// { dg-do run { target c++11 } }
+// { dg-additional-options -O2 }
+
+int a, b, c, d;
+struct A { A () { ++a; ++d; }; ~A () { --a; ++d; }; };
+struct B { B (const A & = A {}) { ++b; ++d; }; ~B () { --b; ++d; }; };
+struct C {
+  C (const B &, const A & = A {}) { ++c; ++d; throw 42; };
+  ~C () { --c; ++d; };
+  int *begin () { return nullptr; };
+  int *end () { return nullptr; };
+};
+
+void
+foo ()
+{
+  for (auto &i : C { B {} })
+    ;
+}
+
+int
+main ()
+{
+  try
+    {
+      foo ();
+      __builtin_abort ();
+    }
+  catch (int x)
+    {
+      if (x != 42 || a || b || c != 1 || d != 7)
+        __builtin_abort ();
+    }
+}

base-commit: 7c50f95421b6de50e4f40773558d0072eafb2365
-- 
2.48.1

Reply via email to