Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?

-- >8 --
The problem in this PR is that we ended up with

  {.rows=(&<PLACEHOLDER_EXPR struct Widget>)->n,
   .outer_stride=(&<PLACEHOLDER_EXPR struct MatrixLayout>)->rows}

that is, two PLACEHOLDER_EXPRs for different types on the same level
in one { }.  That should not happen; we may, for instance, neglect to
replace a PLACEHOLDER_EXPR due to CONSTRUCTOR_PLACEHOLDER_BOUNDARY on
the constructor.

The same problem happened in PR100252, which I fixed by introducing
replace_placeholders_for_class_temp_r.  That didn't work here, though,
because r_p_for_c_t_r only works for non-eliding TARGET_EXPRs: replacing
a PLACEHOLDER_EXPR with a temporary that is going to be elided will
result in a crash in gimplify_var_or_parm_decl when it encounters such
a loose decl.

But leaving the PLACEHOLDER_EXPRs in is also bad because then we end
up with this PR.

TARGET_EXPRs for function arguments are elided in gimplify_arg.  The
argument will get a real temporary only in get_formal_tmp_var.  One
idea was to use the temporary that is going to be elided anyway, and
then replace_decl it with the real object once we get it.  But that
didn't work out: one problem is that we elide the TARGET_EXPR for an
argument before we create the real temporary for the argument, and
when we get it, the context that this was a TARGET_EXPR for an argument
has been lost.  We're also in the middle end territory now, even though
this is a C++-specific problem.

I figured that since the to-be-elided temporary is going to stay around
until gimplification, the front end is free to use it.  Once we're done
with things like store_init_value, which replaces PLACEHOLDER_EXPRs with
the decl it is initializing, we can turn those to-be-elided temporaries
into PLACEHOLDER_EXPRs again, so that cp_gimplify_init_expr can replace
them with the real object once available.  The context is not lost so we
do not need an extra flag for these makeshift temporaries.

        PR c++/116015

gcc/cp/ChangeLog:

        * cp-gimplify.cc (replace_argument_temps_with_placeholders): New.
        (cp_genericize_r) <case CALL_EXPR>: Call it.
        * typeck2.cc (replace_placeholders_for_class_temp_r): Do replace
        placeholders in TARGET_EXPRs for function arguments.

gcc/testsuite/ChangeLog:

        * g++.dg/cpp1y/nsdmi-aggr23.C: New test.
---
 gcc/cp/cp-gimplify.cc                     | 24 +++++++++++++++++++++
 gcc/cp/typeck2.cc                         | 23 ++++++++++++++++++++
 gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr23.C | 26 +++++++++++++++++++++++
 3 files changed, 73 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr23.C

diff --git a/gcc/cp/cp-gimplify.cc b/gcc/cp/cp-gimplify.cc
index 003e68f1ea7..7f203cd6804 100644
--- a/gcc/cp/cp-gimplify.cc
+++ b/gcc/cp/cp-gimplify.cc
@@ -1597,6 +1597,29 @@ predeclare_vla (tree expr)
     }
 }
 
+/* Replace the temporaries used for function arguments with PLACEHOLDER_EXPRs
+   so that they can be replaced again.  See the comment in
+   replace_placeholders_for_class_temp_r for more context.  CALL is either
+   the CALL_EXPR or AGGR_INIT_EXPR whose arguments we're going to adjust.  */
+
+static void
+replace_argument_temps_with_placeholders (tree call)
+{
+  for (int i = 0; i < call_expr_nargs (call); ++i)
+    {
+      tree arg = get_nth_callarg (call, i);
+      if (TREE_CODE (arg) == TARGET_EXPR && TARGET_EXPR_ELIDING_P (arg))
+       {
+         tree slot = TARGET_EXPR_SLOT (arg);
+         tree placeholder = build0 (PLACEHOLDER_EXPR, TREE_TYPE (slot));
+         tree &init = TARGET_EXPR_INITIAL (arg);
+         if (replace_decl (&init, slot, placeholder)
+             && TREE_CODE (init) == CONSTRUCTOR)
+           CONSTRUCTOR_PLACEHOLDER_BOUNDARY (init) = true;
+       }
+    }
+}
+
 /* Perform any pre-gimplification lowering of C++ front end trees to
    GENERIC.  */
 
@@ -2125,6 +2148,7 @@ cp_genericize_r (tree *stmt_p, int *walk_subtrees, void 
*data)
         version is inlinable, a direct call to this version can be made
         otherwise the call should go through the dispatcher.  */
       {
+       replace_argument_temps_with_placeholders (stmt);
        tree fn = cp_get_callee_fndecl_nofold (stmt);
        if (fn && DECL_FUNCTION_VERSIONED (fn)
            && (current_function_decl == NULL
diff --git a/gcc/cp/typeck2.cc b/gcc/cp/typeck2.cc
index 30a6fbe95c9..9b6109ac3ff 100644
--- a/gcc/cp/typeck2.cc
+++ b/gcc/cp/typeck2.cc
@@ -1425,6 +1425,29 @@ replace_placeholders_for_class_temp_r (tree *tp, int *, 
void *)
 {
   tree t = *tp;
 
+  /* If a TARGET_EXPR is an initializer of a function argument, it is
+     going to be elided in gimplify_arg.  So, we should not be using
+     its slot to replace the PLACEHOLDER_EXPR.  But we will only have
+     the real object once we've gimplified the argument, which is too
+     late: we should replace the PLACEHOLDER_EXPR now to avoid winding
+     up with two different PLACEHOLDER_EXPRs in a single {} (c++/116015).
+
+     We can get away with using the temporary...temporarily, because it's
+     going to survive at least until genericization, where we replace it
+     with a PLACEHOLDER_EXPR again, so that it can be replaced with a real
+     object once the argument has been gimplified.  */
+  if (TREE_CODE (t) == CALL_EXPR || TREE_CODE (t) == AGGR_INIT_EXPR)
+    for (int i = 0; i < call_expr_nargs (t); ++i)
+      {
+       tree arg = get_nth_callarg (t, i);
+       if (TREE_CODE (arg) == TARGET_EXPR && TARGET_EXPR_ELIDING_P (arg))
+         {
+           TARGET_EXPR_ELIDING_P (arg) = false;
+           replace_placeholders_for_class_temp_r (&arg, nullptr, nullptr);
+           TARGET_EXPR_ELIDING_P (arg) = true;
+         }
+      }
+
   /* We're looking for a TARGET_EXPR nested in the whole expression.  */
   if (TREE_CODE (t) == TARGET_EXPR
       /* That serves as temporary materialization, not an initializer.  */
diff --git a/gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr23.C 
b/gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr23.C
new file mode 100644
index 00000000000..2f5b8ca97bf
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr23.C
@@ -0,0 +1,26 @@
+// PR c++/116015
+// { dg-do compile { target c++14 } }
+// { dg-additional-options "-Wno-c++20-extensions" }
+
+struct MatrixLayout {
+    int rows         = 0;
+    int outer_stride = rows;
+};
+struct Matrix {
+    Matrix(MatrixLayout m) {}
+};
+struct Widget {
+    int n = 5;
+    Matrix A0{{}};
+    Matrix A1{{n}};
+    Matrix A1_{{.rows = n}};
+    Matrix A2{{n, n}};
+};
+
+int
+main ()
+{
+ Widget w{};
+ Widget w1;
+ Widget w2 = {};
+}

base-commit: c4d3dba253b49fb0e8e32109783f76453bc53653
-- 
2.45.2

Reply via email to