public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH] c++: ICE with temporary of class type in array DMI [PR109966]
@ 2024-03-11 23:27 Marek Polacek
  2024-03-12 13:57 ` Jason Merrill
  0 siblings, 1 reply; 8+ messages in thread
From: Marek Polacek @ 2024-03-11 23:27 UTC (permalink / raw)
  To: Jason Merrill, GCC Patches

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

-- >8 --
This ICE started with the fairly complicated r13-765.  We crash in
gimplify_var_or_parm_decl because a stray VAR_DECL leaked there.
The problem is ultimately that potential_prvalue_result_of wasn't
correctly handling arrays and replace_placeholders_for_class_temp_r
replaced a PLACEHOLDER_EXPR in a TARGET_EXPR which is used in the
context of copy elision.  If I have

  M m[2] = { M{""}, M{""} };

then we don't invoke the M(const M&) copy-ctor.  I think the fix is
to detect such a case in potential_prvalue_result_of.

	PR c++/109966

gcc/cp/ChangeLog:

	* typeck2.cc (potential_prvalue_result_of): Add walk_subtrees
	parameter.  Handle initializing an array from a
	brace-enclosed-initializer.
	(replace_placeholders_for_class_temp_r): Pass walk_subtrees down to
	potential_prvalue_result_of.

gcc/testsuite/ChangeLog:

	* g++.dg/cpp1y/nsdmi-aggr20.C: New test.
	* g++.dg/cpp1y/nsdmi-aggr21.C: New test.
---
 gcc/cp/typeck2.cc                         | 27 ++++++++---
 gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr20.C | 17 +++++++
 gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr21.C | 59 +++++++++++++++++++++++
 3 files changed, 96 insertions(+), 7 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr20.C
 create mode 100644 gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr21.C

diff --git a/gcc/cp/typeck2.cc b/gcc/cp/typeck2.cc
index 31198b2f9f5..8b99ce78e9a 100644
--- a/gcc/cp/typeck2.cc
+++ b/gcc/cp/typeck2.cc
@@ -1406,46 +1406,59 @@ digest_init_flags (tree type, tree init, int flags, tsubst_flags_t complain)
      A a = (A{});	      // initializer
      A a = (1, A{});	      // initializer
      A a = true ? A{} : A{};  // initializer
+     A arr[1] = { A{} };      // initializer
      auto x = A{}.x;	      // temporary materialization
      auto x = foo(A{});	      // temporary materialization
 
    FULL_EXPR is the whole expression, SUBOB is its TARGET_EXPR subobject.  */
 
 static bool
-potential_prvalue_result_of (tree subob, tree full_expr)
+potential_prvalue_result_of (tree subob, tree full_expr, int *walk_subtrees)
 {
+#define RECUR(t) potential_prvalue_result_of (subob, t, walk_subtrees)
   if (subob == full_expr)
     return true;
   else if (TREE_CODE (full_expr) == TARGET_EXPR)
     {
       tree init = TARGET_EXPR_INITIAL (full_expr);
       if (TREE_CODE (init) == COND_EXPR)
-	return (potential_prvalue_result_of (subob, TREE_OPERAND (init, 1))
-		|| potential_prvalue_result_of (subob, TREE_OPERAND (init, 2)));
+	return (RECUR (TREE_OPERAND (init, 1))
+		|| RECUR (TREE_OPERAND (init, 2)));
       else if (TREE_CODE (init) == COMPOUND_EXPR)
-	return potential_prvalue_result_of (subob, TREE_OPERAND (init, 1));
+	return RECUR (TREE_OPERAND (init, 1));
       /* ??? I don't know if this can be hit.  */
       else if (TREE_CODE (init) == PAREN_EXPR)
 	{
 	  gcc_checking_assert (false);
-	  return potential_prvalue_result_of (subob, TREE_OPERAND (init, 0));
+	  return RECUR (TREE_OPERAND (init, 0));
 	}
     }
+  /* The array case listed above.  */
+  else if (TREE_CODE (full_expr) == CONSTRUCTOR
+	   && TREE_CODE (TREE_TYPE (full_expr)) == ARRAY_TYPE)
+    for (constructor_elt &e: CONSTRUCTOR_ELTS (full_expr))
+      if (e.value == subob)
+	{
+	  *walk_subtrees = 0;
+	  return true;
+	}
+
   return false;
+#undef RECUR
 }
 
 /* Callback to replace PLACEHOLDER_EXPRs in a TARGET_EXPR (which isn't used
    in the context of guaranteed copy elision).  */
 
 static tree
-replace_placeholders_for_class_temp_r (tree *tp, int *, void *data)
+replace_placeholders_for_class_temp_r (tree *tp, int *walk_subtrees, void *data)
 {
   tree t = *tp;
   tree full_expr = *static_cast<tree *>(data);
 
   /* We're looking for a TARGET_EXPR nested in the whole expression.  */
   if (TREE_CODE (t) == TARGET_EXPR
-      && !potential_prvalue_result_of (t, full_expr))
+      && !potential_prvalue_result_of (t, full_expr, walk_subtrees))
     {
       tree init = TARGET_EXPR_INITIAL (t);
       while (TREE_CODE (init) == COMPOUND_EXPR)
diff --git a/gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr20.C b/gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr20.C
new file mode 100644
index 00000000000..4796d861e83
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr20.C
@@ -0,0 +1,17 @@
+// PR c++/109966
+// { dg-do compile { target c++14 } }
+
+#define SA(X) static_assert ((X),#X)
+
+struct A {
+  int a;
+  int b = a;
+};
+
+struct B {
+  int x = 0;
+  int y[1]{A{x}.b};
+};
+
+constexpr B b = { };
+SA(b.y[0] == 0);
diff --git a/gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr21.C b/gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr21.C
new file mode 100644
index 00000000000..efec45bc1a8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr21.C
@@ -0,0 +1,59 @@
+// PR c++/109966
+// { dg-do compile { target c++14 } }
+
+struct k {
+  k(const char *);
+};
+struct M {
+  k name;
+  int j = 42;
+  int i = j;
+};
+
+M m = M{""};
+
+struct S {
+  M arr1[1]{M{""}};
+  M a1[1] = { (M{""}) };
+  M a2[1] = { (true, M{""}) };
+  M a3[1] = { true ? M{""} : M{""} };
+  M arr2[2]{M{""}, M{""}};
+  M arr3[3]{M{""}, M{""}, M{""}};
+
+  M arr1e[1] = {M{""}};
+  M arr2e[2] = {M{""}, M{""}};
+  M arr3e[3] = {M{""}, M{""}, M{""}};
+
+  M arr1l[1] = { m };
+  M arr2l[2] = { m, m };
+  M arr3l[3] = { m, m, m };
+
+  M m1 = M{""};
+  M m2 = m;
+  M m3{M{""}};
+  M m4 = {M{""}};
+} o;
+
+struct N {
+  N(M);
+};
+
+struct Z {
+  N arr1[1]{ M{""} };
+  N arr2[2]{ M{""}, M{""} };
+  N arr1e[1] = { M{""} };
+  N arr2e[2] = { M{""}, M{""} };
+} z;
+
+struct Y {
+  k name;
+  int j = 42;
+  int i = j;
+  operator M();
+};
+
+struct W {
+  M arr1[1]{ Y{""} };
+  M arr2[2]{ Y{""}, Y{""} };
+  M arr3[3]{ Y{""}, Y{""}, Y{""} };
+} w;

base-commit: 0c179654c3170749f3fb3232f2442fcbc99bffbb
-- 
2.44.0


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] c++: ICE with temporary of class type in array DMI [PR109966]
  2024-03-11 23:27 [PATCH] c++: ICE with temporary of class type in array DMI [PR109966] Marek Polacek
@ 2024-03-12 13:57 ` Jason Merrill
  2024-03-12 15:56   ` [PATCH v2] " Marek Polacek
  0 siblings, 1 reply; 8+ messages in thread
From: Jason Merrill @ 2024-03-12 13:57 UTC (permalink / raw)
  To: Marek Polacek, GCC Patches

On 3/11/24 19:27, Marek Polacek wrote:
> Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk/13?
> 
> -- >8 --
> This ICE started with the fairly complicated r13-765.  We crash in
> gimplify_var_or_parm_decl because a stray VAR_DECL leaked there.
> The problem is ultimately that potential_prvalue_result_of wasn't
> correctly handling arrays and replace_placeholders_for_class_temp_r
> replaced a PLACEHOLDER_EXPR in a TARGET_EXPR which is used in the
> context of copy elision.  If I have
> 
>    M m[2] = { M{""}, M{""} };
> 
> then we don't invoke the M(const M&) copy-ctor.  I think the fix is
> to detect such a case in potential_prvalue_result_of.
> 
> 	PR c++/109966
> 
> gcc/cp/ChangeLog:
> 
> 	* typeck2.cc (potential_prvalue_result_of): Add walk_subtrees
> 	parameter.  Handle initializing an array from a
> 	brace-enclosed-initializer.
> 	(replace_placeholders_for_class_temp_r): Pass walk_subtrees down to
> 	potential_prvalue_result_of.
> 
> gcc/testsuite/ChangeLog:
> 
> 	* g++.dg/cpp1y/nsdmi-aggr20.C: New test.
> 	* g++.dg/cpp1y/nsdmi-aggr21.C: New test.
> ---
>   gcc/cp/typeck2.cc                         | 27 ++++++++---
>   gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr20.C | 17 +++++++
>   gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr21.C | 59 +++++++++++++++++++++++
>   3 files changed, 96 insertions(+), 7 deletions(-)
>   create mode 100644 gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr20.C
>   create mode 100644 gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr21.C
> 
> diff --git a/gcc/cp/typeck2.cc b/gcc/cp/typeck2.cc
> index 31198b2f9f5..8b99ce78e9a 100644
> --- a/gcc/cp/typeck2.cc
> +++ b/gcc/cp/typeck2.cc
> @@ -1406,46 +1406,59 @@ digest_init_flags (tree type, tree init, int flags, tsubst_flags_t complain)
>        A a = (A{});	      // initializer
>        A a = (1, A{});	      // initializer
>        A a = true ? A{} : A{};  // initializer
> +     A arr[1] = { A{} };      // initializer
>        auto x = A{}.x;	      // temporary materialization
>        auto x = foo(A{});	      // temporary materialization
>   
>      FULL_EXPR is the whole expression, SUBOB is its TARGET_EXPR subobject.  */
>   
>   static bool
> -potential_prvalue_result_of (tree subob, tree full_expr)
> +potential_prvalue_result_of (tree subob, tree full_expr, int *walk_subtrees)
>   {
> +#define RECUR(t) potential_prvalue_result_of (subob, t, walk_subtrees)
>     if (subob == full_expr)
>       return true;
>     else if (TREE_CODE (full_expr) == TARGET_EXPR)
>       {
>         tree init = TARGET_EXPR_INITIAL (full_expr);
>         if (TREE_CODE (init) == COND_EXPR)
> -	return (potential_prvalue_result_of (subob, TREE_OPERAND (init, 1))
> -		|| potential_prvalue_result_of (subob, TREE_OPERAND (init, 2)));
> +	return (RECUR (TREE_OPERAND (init, 1))
> +		|| RECUR (TREE_OPERAND (init, 2)));
>         else if (TREE_CODE (init) == COMPOUND_EXPR)
> -	return potential_prvalue_result_of (subob, TREE_OPERAND (init, 1));
> +	return RECUR (TREE_OPERAND (init, 1));
>         /* ??? I don't know if this can be hit.  */
>         else if (TREE_CODE (init) == PAREN_EXPR)
>   	{
>   	  gcc_checking_assert (false);
> -	  return potential_prvalue_result_of (subob, TREE_OPERAND (init, 0));
> +	  return RECUR (TREE_OPERAND (init, 0));
>   	}
>       }
> +  /* The array case listed above.  */
> +  else if (TREE_CODE (full_expr) == CONSTRUCTOR
> +	   && TREE_CODE (TREE_TYPE (full_expr)) == ARRAY_TYPE)
> +    for (constructor_elt &e: CONSTRUCTOR_ELTS (full_expr))
> +      if (e.value == subob)
> +	{
> +	  *walk_subtrees = 0;

Why clear walk_subtrees?  Won't that mean we fail to replace any 
placeholders nested within an array element initializer?

Jason


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH v2] c++: ICE with temporary of class type in array DMI [PR109966]
  2024-03-12 13:57 ` Jason Merrill
@ 2024-03-12 15:56   ` Marek Polacek
  2024-03-12 22:26     ` Jason Merrill
  0 siblings, 1 reply; 8+ messages in thread
From: Marek Polacek @ 2024-03-12 15:56 UTC (permalink / raw)
  To: Jason Merrill; +Cc: GCC Patches

On Tue, Mar 12, 2024 at 09:57:14AM -0400, Jason Merrill wrote:
> On 3/11/24 19:27, Marek Polacek wrote:
> > Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk/13?
> > 
> > -- >8 --
> > This ICE started with the fairly complicated r13-765.  We crash in
> > gimplify_var_or_parm_decl because a stray VAR_DECL leaked there.
> > The problem is ultimately that potential_prvalue_result_of wasn't
> > correctly handling arrays and replace_placeholders_for_class_temp_r
> > replaced a PLACEHOLDER_EXPR in a TARGET_EXPR which is used in the
> > context of copy elision.  If I have
> > 
> >    M m[2] = { M{""}, M{""} };
> > 
> > then we don't invoke the M(const M&) copy-ctor.  I think the fix is
> > to detect such a case in potential_prvalue_result_of.
> > 
> > 	PR c++/109966
> > 
> > gcc/cp/ChangeLog:
> > 
> > 	* typeck2.cc (potential_prvalue_result_of): Add walk_subtrees
> > 	parameter.  Handle initializing an array from a
> > 	brace-enclosed-initializer.
> > 	(replace_placeholders_for_class_temp_r): Pass walk_subtrees down to
> > 	potential_prvalue_result_of.
> > 
> > gcc/testsuite/ChangeLog:
> > 
> > 	* g++.dg/cpp1y/nsdmi-aggr20.C: New test.
> > 	* g++.dg/cpp1y/nsdmi-aggr21.C: New test.
> > ---
> >   gcc/cp/typeck2.cc                         | 27 ++++++++---
> >   gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr20.C | 17 +++++++
> >   gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr21.C | 59 +++++++++++++++++++++++
> >   3 files changed, 96 insertions(+), 7 deletions(-)
> >   create mode 100644 gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr20.C
> >   create mode 100644 gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr21.C
> > 
> > diff --git a/gcc/cp/typeck2.cc b/gcc/cp/typeck2.cc
> > index 31198b2f9f5..8b99ce78e9a 100644
> > --- a/gcc/cp/typeck2.cc
> > +++ b/gcc/cp/typeck2.cc
> > @@ -1406,46 +1406,59 @@ digest_init_flags (tree type, tree init, int flags, tsubst_flags_t complain)
> >        A a = (A{});	      // initializer
> >        A a = (1, A{});	      // initializer
> >        A a = true ? A{} : A{};  // initializer
> > +     A arr[1] = { A{} };      // initializer
> >        auto x = A{}.x;	      // temporary materialization
> >        auto x = foo(A{});	      // temporary materialization
> >      FULL_EXPR is the whole expression, SUBOB is its TARGET_EXPR subobject.  */
> >   static bool
> > -potential_prvalue_result_of (tree subob, tree full_expr)
> > +potential_prvalue_result_of (tree subob, tree full_expr, int *walk_subtrees)
> >   {
> > +#define RECUR(t) potential_prvalue_result_of (subob, t, walk_subtrees)
> >     if (subob == full_expr)
> >       return true;
> >     else if (TREE_CODE (full_expr) == TARGET_EXPR)
> >       {
> >         tree init = TARGET_EXPR_INITIAL (full_expr);
> >         if (TREE_CODE (init) == COND_EXPR)
> > -	return (potential_prvalue_result_of (subob, TREE_OPERAND (init, 1))
> > -		|| potential_prvalue_result_of (subob, TREE_OPERAND (init, 2)));
> > +	return (RECUR (TREE_OPERAND (init, 1))
> > +		|| RECUR (TREE_OPERAND (init, 2)));
> >         else if (TREE_CODE (init) == COMPOUND_EXPR)
> > -	return potential_prvalue_result_of (subob, TREE_OPERAND (init, 1));
> > +	return RECUR (TREE_OPERAND (init, 1));
> >         /* ??? I don't know if this can be hit.  */
> >         else if (TREE_CODE (init) == PAREN_EXPR)
> >   	{
> >   	  gcc_checking_assert (false);
> > -	  return potential_prvalue_result_of (subob, TREE_OPERAND (init, 0));
> > +	  return RECUR (TREE_OPERAND (init, 0));
> >   	}
> >       }
> > +  /* The array case listed above.  */
> > +  else if (TREE_CODE (full_expr) == CONSTRUCTOR
> > +	   && TREE_CODE (TREE_TYPE (full_expr)) == ARRAY_TYPE)
> > +    for (constructor_elt &e: CONSTRUCTOR_ELTS (full_expr))
> > +      if (e.value == subob)
> > +	{
> > +	  *walk_subtrees = 0;
> 
> Why clear walk_subtrees?  Won't that mean we fail to replace any
> placeholders nested within an array element initializer?

Right.  I couldn't find a testcase where that would cause a problem
but I think I just wasn't inventive enough.

Originally, I was checking same_type_ignoring_top_level_qualifiers_p
but that's not going to work for code like

  struct N { N(M); };
  N arr[2] = { M{""}, M{""} };

or with operator M().  But I suppose I could just use can_convert
like below.  What do you think about that?

dg.exp passed, full regtest running.

-- >8 --
This ICE started with the fairly complicated r13-765.  We crash in
gimplify_var_or_parm_decl because a stray VAR_DECL leaked there.
The problem is ultimately that potential_prvalue_result_of wasn't
correctly handling arrays and replace_placeholders_for_class_temp_r
replaced a PLACEHOLDER_EXPR in a TARGET_EXPR which is used in the
context of copy elision.  If I have

  M m[2] = { M{""}, M{""} };

then we don't invoke the M(const M&) copy-ctor.  I think the fix is
to detect such a case in potential_prvalue_result_of.

	PR c++/109966

gcc/cp/ChangeLog:

	* typeck2.cc (potential_prvalue_result_of): Handle initializing an
	array from a brace-enclosed-initializer.

gcc/testsuite/ChangeLog:

	* g++.dg/cpp1y/nsdmi-aggr20.C: New test.
	* g++.dg/cpp1y/nsdmi-aggr21.C: New test.
---
 gcc/cp/typeck2.cc                         | 18 +++++--
 gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr20.C | 17 +++++++
 gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr21.C | 59 +++++++++++++++++++++++
 3 files changed, 90 insertions(+), 4 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr20.C
 create mode 100644 gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr21.C

diff --git a/gcc/cp/typeck2.cc b/gcc/cp/typeck2.cc
index 31198b2f9f5..21d4f42ae20 100644
--- a/gcc/cp/typeck2.cc
+++ b/gcc/cp/typeck2.cc
@@ -1406,6 +1406,7 @@ digest_init_flags (tree type, tree init, int flags, tsubst_flags_t complain)
      A a = (A{});	      // initializer
      A a = (1, A{});	      // initializer
      A a = true ? A{} : A{};  // initializer
+     A arr[1] = { A{} };      // initializer
      auto x = A{}.x;	      // temporary materialization
      auto x = foo(A{});	      // temporary materialization
 
@@ -1414,24 +1415,33 @@ digest_init_flags (tree type, tree init, int flags, tsubst_flags_t complain)
 static bool
 potential_prvalue_result_of (tree subob, tree full_expr)
 {
+#define RECUR(t) potential_prvalue_result_of (subob, t)
   if (subob == full_expr)
     return true;
   else if (TREE_CODE (full_expr) == TARGET_EXPR)
     {
       tree init = TARGET_EXPR_INITIAL (full_expr);
       if (TREE_CODE (init) == COND_EXPR)
-	return (potential_prvalue_result_of (subob, TREE_OPERAND (init, 1))
-		|| potential_prvalue_result_of (subob, TREE_OPERAND (init, 2)));
+	return (RECUR (TREE_OPERAND (init, 1))
+		|| RECUR (TREE_OPERAND (init, 2)));
       else if (TREE_CODE (init) == COMPOUND_EXPR)
-	return potential_prvalue_result_of (subob, TREE_OPERAND (init, 1));
+	return RECUR (TREE_OPERAND (init, 1));
       /* ??? I don't know if this can be hit.  */
       else if (TREE_CODE (init) == PAREN_EXPR)
 	{
 	  gcc_checking_assert (false);
-	  return potential_prvalue_result_of (subob, TREE_OPERAND (init, 0));
+	  return RECUR (TREE_OPERAND (init, 0));
 	}
     }
+  /* The array case listed above.  */
+  else if (TREE_CODE (full_expr) == CONSTRUCTOR
+	   && TREE_CODE (TREE_TYPE (full_expr)) == ARRAY_TYPE
+	   && can_convert (strip_array_types (TREE_TYPE (full_expr)),
+			   TREE_TYPE (subob), tf_none))
+      return true;
+
   return false;
+#undef RECUR
 }
 
 /* Callback to replace PLACEHOLDER_EXPRs in a TARGET_EXPR (which isn't used
diff --git a/gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr20.C b/gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr20.C
new file mode 100644
index 00000000000..4796d861e83
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr20.C
@@ -0,0 +1,17 @@
+// PR c++/109966
+// { dg-do compile { target c++14 } }
+
+#define SA(X) static_assert ((X),#X)
+
+struct A {
+  int a;
+  int b = a;
+};
+
+struct B {
+  int x = 0;
+  int y[1]{A{x}.b};
+};
+
+constexpr B b = { };
+SA(b.y[0] == 0);
diff --git a/gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr21.C b/gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr21.C
new file mode 100644
index 00000000000..efec45bc1a8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr21.C
@@ -0,0 +1,59 @@
+// PR c++/109966
+// { dg-do compile { target c++14 } }
+
+struct k {
+  k(const char *);
+};
+struct M {
+  k name;
+  int j = 42;
+  int i = j;
+};
+
+M m = M{""};
+
+struct S {
+  M arr1[1]{M{""}};
+  M a1[1] = { (M{""}) };
+  M a2[1] = { (true, M{""}) };
+  M a3[1] = { true ? M{""} : M{""} };
+  M arr2[2]{M{""}, M{""}};
+  M arr3[3]{M{""}, M{""}, M{""}};
+
+  M arr1e[1] = {M{""}};
+  M arr2e[2] = {M{""}, M{""}};
+  M arr3e[3] = {M{""}, M{""}, M{""}};
+
+  M arr1l[1] = { m };
+  M arr2l[2] = { m, m };
+  M arr3l[3] = { m, m, m };
+
+  M m1 = M{""};
+  M m2 = m;
+  M m3{M{""}};
+  M m4 = {M{""}};
+} o;
+
+struct N {
+  N(M);
+};
+
+struct Z {
+  N arr1[1]{ M{""} };
+  N arr2[2]{ M{""}, M{""} };
+  N arr1e[1] = { M{""} };
+  N arr2e[2] = { M{""}, M{""} };
+} z;
+
+struct Y {
+  k name;
+  int j = 42;
+  int i = j;
+  operator M();
+};
+
+struct W {
+  M arr1[1]{ Y{""} };
+  M arr2[2]{ Y{""}, Y{""} };
+  M arr3[3]{ Y{""}, Y{""}, Y{""} };
+} w;

base-commit: ef79c64cb5762c86ee04ddfcedb7fe31eaa3bac8
-- 
2.44.0


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v2] c++: ICE with temporary of class type in array DMI [PR109966]
  2024-03-12 15:56   ` [PATCH v2] " Marek Polacek
@ 2024-03-12 22:26     ` Jason Merrill
  2024-03-14 21:26       ` [PATCH v3] " Marek Polacek
  0 siblings, 1 reply; 8+ messages in thread
From: Jason Merrill @ 2024-03-12 22:26 UTC (permalink / raw)
  To: Marek Polacek; +Cc: GCC Patches

On 3/12/24 11:56, Marek Polacek wrote:
> On Tue, Mar 12, 2024 at 09:57:14AM -0400, Jason Merrill wrote:
>> On 3/11/24 19:27, Marek Polacek wrote:
>>> Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk/13?
>>>
>>> -- >8 --
>>> This ICE started with the fairly complicated r13-765.  We crash in
>>> gimplify_var_or_parm_decl because a stray VAR_DECL leaked there.
>>> The problem is ultimately that potential_prvalue_result_of wasn't
>>> correctly handling arrays and replace_placeholders_for_class_temp_r
>>> replaced a PLACEHOLDER_EXPR in a TARGET_EXPR which is used in the
>>> context of copy elision.  If I have
>>>
>>>     M m[2] = { M{""}, M{""} };
>>>
>>> then we don't invoke the M(const M&) copy-ctor.  I think the fix is
>>> to detect such a case in potential_prvalue_result_of.
>>>
>>> 	PR c++/109966
>>>
>>> gcc/cp/ChangeLog:
>>>
>>> 	* typeck2.cc (potential_prvalue_result_of): Add walk_subtrees
>>> 	parameter.  Handle initializing an array from a
>>> 	brace-enclosed-initializer.
>>> 	(replace_placeholders_for_class_temp_r): Pass walk_subtrees down to
>>> 	potential_prvalue_result_of.
>>>
>>> gcc/testsuite/ChangeLog:
>>>
>>> 	* g++.dg/cpp1y/nsdmi-aggr20.C: New test.
>>> 	* g++.dg/cpp1y/nsdmi-aggr21.C: New test.
>>> ---
>>>    gcc/cp/typeck2.cc                         | 27 ++++++++---
>>>    gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr20.C | 17 +++++++
>>>    gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr21.C | 59 +++++++++++++++++++++++
>>>    3 files changed, 96 insertions(+), 7 deletions(-)
>>>    create mode 100644 gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr20.C
>>>    create mode 100644 gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr21.C
>>>
>>> diff --git a/gcc/cp/typeck2.cc b/gcc/cp/typeck2.cc
>>> index 31198b2f9f5..8b99ce78e9a 100644
>>> --- a/gcc/cp/typeck2.cc
>>> +++ b/gcc/cp/typeck2.cc
>>> @@ -1406,46 +1406,59 @@ digest_init_flags (tree type, tree init, int flags, tsubst_flags_t complain)
>>>         A a = (A{});	      // initializer
>>>         A a = (1, A{});	      // initializer
>>>         A a = true ? A{} : A{};  // initializer
>>> +     A arr[1] = { A{} };      // initializer
>>>         auto x = A{}.x;	      // temporary materialization
>>>         auto x = foo(A{});	      // temporary materialization
>>>       FULL_EXPR is the whole expression, SUBOB is its TARGET_EXPR subobject.  */
>>>    static bool
>>> -potential_prvalue_result_of (tree subob, tree full_expr)
>>> +potential_prvalue_result_of (tree subob, tree full_expr, int *walk_subtrees)
>>>    {
>>> +#define RECUR(t) potential_prvalue_result_of (subob, t, walk_subtrees)
>>>      if (subob == full_expr)
>>>        return true;
>>>      else if (TREE_CODE (full_expr) == TARGET_EXPR)
>>>        {
>>>          tree init = TARGET_EXPR_INITIAL (full_expr);
>>>          if (TREE_CODE (init) == COND_EXPR)
>>> -	return (potential_prvalue_result_of (subob, TREE_OPERAND (init, 1))
>>> -		|| potential_prvalue_result_of (subob, TREE_OPERAND (init, 2)));
>>> +	return (RECUR (TREE_OPERAND (init, 1))
>>> +		|| RECUR (TREE_OPERAND (init, 2)));
>>>          else if (TREE_CODE (init) == COMPOUND_EXPR)
>>> -	return potential_prvalue_result_of (subob, TREE_OPERAND (init, 1));
>>> +	return RECUR (TREE_OPERAND (init, 1));
>>>          /* ??? I don't know if this can be hit.  */
>>>          else if (TREE_CODE (init) == PAREN_EXPR)
>>>    	{
>>>    	  gcc_checking_assert (false);
>>> -	  return potential_prvalue_result_of (subob, TREE_OPERAND (init, 0));
>>> +	  return RECUR (TREE_OPERAND (init, 0));
>>>    	}
>>>        }
>>> +  /* The array case listed above.  */
>>> +  else if (TREE_CODE (full_expr) == CONSTRUCTOR
>>> +	   && TREE_CODE (TREE_TYPE (full_expr)) == ARRAY_TYPE)
>>> +    for (constructor_elt &e: CONSTRUCTOR_ELTS (full_expr))
>>> +      if (e.value == subob)
>>> +	{
>>> +	  *walk_subtrees = 0;
>>
>> Why clear walk_subtrees?  Won't that mean we fail to replace any
>> placeholders nested within an array element initializer?
> 
> Right.  I couldn't find a testcase where that would cause a problem
> but I think I just wasn't inventive enough.
> 
> Originally, I was checking same_type_ignoring_top_level_qualifiers_p
> but that's not going to work for code like
> 
>    struct N { N(M); };
>    N arr[2] = { M{""}, M{""} };
> 
> or with operator M().  But I suppose I could just use can_convert
> like below.  What do you think about that?

Basing this on the type seems unreliable, we're looking for where in the 
expression the TARGET_EXPR occurs, and there could be others of the same 
type elsewhere in the expression.

Why not loop over CONSTRUCTOR_ELTS like you did above, just without 
clearing walk_subtrees?

Jason


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH v3] c++: ICE with temporary of class type in array DMI [PR109966]
  2024-03-12 22:26     ` Jason Merrill
@ 2024-03-14 21:26       ` Marek Polacek
  2024-03-19 19:47         ` Marek Polacek
  2024-04-12 20:15         ` Jason Merrill
  0 siblings, 2 replies; 8+ messages in thread
From: Marek Polacek @ 2024-03-14 21:26 UTC (permalink / raw)
  To: Jason Merrill; +Cc: GCC Patches

On Tue, Mar 12, 2024 at 06:26:14PM -0400, Jason Merrill wrote:
> On 3/12/24 11:56, Marek Polacek wrote:
> > On Tue, Mar 12, 2024 at 09:57:14AM -0400, Jason Merrill wrote:
> > > On 3/11/24 19:27, Marek Polacek wrote:
> > > > Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk/13?
> > > > 
> > > > -- >8 --
> > > > This ICE started with the fairly complicated r13-765.  We crash in
> > > > gimplify_var_or_parm_decl because a stray VAR_DECL leaked there.
> > > > The problem is ultimately that potential_prvalue_result_of wasn't
> > > > correctly handling arrays and replace_placeholders_for_class_temp_r
> > > > replaced a PLACEHOLDER_EXPR in a TARGET_EXPR which is used in the
> > > > context of copy elision.  If I have
> > > > 
> > > >     M m[2] = { M{""}, M{""} };
> > > > 
> > > > then we don't invoke the M(const M&) copy-ctor.  I think the fix is
> > > > to detect such a case in potential_prvalue_result_of.
> > > > 
> > > > 	PR c++/109966
> > > > 
> > > > gcc/cp/ChangeLog:
> > > > 
> > > > 	* typeck2.cc (potential_prvalue_result_of): Add walk_subtrees
> > > > 	parameter.  Handle initializing an array from a
> > > > 	brace-enclosed-initializer.
> > > > 	(replace_placeholders_for_class_temp_r): Pass walk_subtrees down to
> > > > 	potential_prvalue_result_of.
> > > > 
> > > > gcc/testsuite/ChangeLog:
> > > > 
> > > > 	* g++.dg/cpp1y/nsdmi-aggr20.C: New test.
> > > > 	* g++.dg/cpp1y/nsdmi-aggr21.C: New test.
> > > > ---
> > > >    gcc/cp/typeck2.cc                         | 27 ++++++++---
> > > >    gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr20.C | 17 +++++++
> > > >    gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr21.C | 59 +++++++++++++++++++++++
> > > >    3 files changed, 96 insertions(+), 7 deletions(-)
> > > >    create mode 100644 gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr20.C
> > > >    create mode 100644 gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr21.C
> > > > 
> > > > diff --git a/gcc/cp/typeck2.cc b/gcc/cp/typeck2.cc
> > > > index 31198b2f9f5..8b99ce78e9a 100644
> > > > --- a/gcc/cp/typeck2.cc
> > > > +++ b/gcc/cp/typeck2.cc
> > > > @@ -1406,46 +1406,59 @@ digest_init_flags (tree type, tree init, int flags, tsubst_flags_t complain)
> > > >         A a = (A{});	      // initializer
> > > >         A a = (1, A{});	      // initializer
> > > >         A a = true ? A{} : A{};  // initializer
> > > > +     A arr[1] = { A{} };      // initializer
> > > >         auto x = A{}.x;	      // temporary materialization
> > > >         auto x = foo(A{});	      // temporary materialization
> > > >       FULL_EXPR is the whole expression, SUBOB is its TARGET_EXPR subobject.  */
> > > >    static bool
> > > > -potential_prvalue_result_of (tree subob, tree full_expr)
> > > > +potential_prvalue_result_of (tree subob, tree full_expr, int *walk_subtrees)
> > > >    {
> > > > +#define RECUR(t) potential_prvalue_result_of (subob, t, walk_subtrees)
> > > >      if (subob == full_expr)
> > > >        return true;
> > > >      else if (TREE_CODE (full_expr) == TARGET_EXPR)
> > > >        {
> > > >          tree init = TARGET_EXPR_INITIAL (full_expr);
> > > >          if (TREE_CODE (init) == COND_EXPR)
> > > > -	return (potential_prvalue_result_of (subob, TREE_OPERAND (init, 1))
> > > > -		|| potential_prvalue_result_of (subob, TREE_OPERAND (init, 2)));
> > > > +	return (RECUR (TREE_OPERAND (init, 1))
> > > > +		|| RECUR (TREE_OPERAND (init, 2)));
> > > >          else if (TREE_CODE (init) == COMPOUND_EXPR)
> > > > -	return potential_prvalue_result_of (subob, TREE_OPERAND (init, 1));
> > > > +	return RECUR (TREE_OPERAND (init, 1));
> > > >          /* ??? I don't know if this can be hit.  */
> > > >          else if (TREE_CODE (init) == PAREN_EXPR)
> > > >    	{
> > > >    	  gcc_checking_assert (false);
> > > > -	  return potential_prvalue_result_of (subob, TREE_OPERAND (init, 0));
> > > > +	  return RECUR (TREE_OPERAND (init, 0));
> > > >    	}
> > > >        }
> > > > +  /* The array case listed above.  */
> > > > +  else if (TREE_CODE (full_expr) == CONSTRUCTOR
> > > > +	   && TREE_CODE (TREE_TYPE (full_expr)) == ARRAY_TYPE)
> > > > +    for (constructor_elt &e: CONSTRUCTOR_ELTS (full_expr))
> > > > +      if (e.value == subob)
> > > > +	{
> > > > +	  *walk_subtrees = 0;
> > > 
> > > Why clear walk_subtrees?  Won't that mean we fail to replace any
> > > placeholders nested within an array element initializer?
> > 
> > Right.  I couldn't find a testcase where that would cause a problem
> > but I think I just wasn't inventive enough.
> > 
> > Originally, I was checking same_type_ignoring_top_level_qualifiers_p
> > but that's not going to work for code like
> > 
> >    struct N { N(M); };
> >    N arr[2] = { M{""}, M{""} };
> > 
> > or with operator M().  But I suppose I could just use can_convert
> > like below.  What do you think about that?
> 
> Basing this on the type seems unreliable, we're looking for where in the
> expression the TARGET_EXPR occurs, and there could be others of the same
> type elsewhere in the expression.
> 
> Why not loop over CONSTRUCTOR_ELTS like you did above, just without clearing
> walk_subtrees?

With the test with N(M) we are dealing with:

{TARGET_EXPR <D.2892, <<< Unknown tree: aggr_init_expr
  5
  __ct_comp 
  D.2892
  (struct N *) <<< Unknown tree: void_cst >>>
  TARGET_EXPR <D.2864, {.name=TARGET_EXPR <D.2854, <<< Unknown tree: aggr_init_expr
    5
    __ct_comp 
    D.2854
    (struct k *) <<< Unknown tree: void_cst >>>
    (const char *) "" >>>>, .j=NON_LVALUE_EXPR <42>, .i=(&<PLACEHOLDER_EXPR struct M>)->j}> >>>>}

where the TARGET_EXPR with slot=D.2864 is the problematic one.  So 
looping over CONSTRUCTOR_ELTS of the outer CONSTRUCTOR wouldn't find
it.  But you're of course right that just checking the type is fragile.

In the following patch, I'm taking a different tack.  I believe
we ought to use TARGET_EXPR_ELIDING_P.  The gimplify_arg bit I'm
talking about below is this:

      /* Also strip a TARGET_EXPR that would force an extra copy.  */
      if (TREE_CODE (*arg_p) == TARGET_EXPR)
        {
          tree init = TARGET_EXPR_INITIAL (*arg_p);
          if (init
              && !VOID_TYPE_P (TREE_TYPE (init)))
            *arg_p = init;
        }

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

-- >8 --
This ICE started with the fairly complicated r13-765.  We crash in
gimplify_var_or_parm_decl because a stray VAR_DECL leaked there.
The problem is ultimately that potential_prvalue_result_of wasn't
correctly handling arrays and replace_placeholders_for_class_temp_r
replaced a PLACEHOLDER_EXPR in a TARGET_EXPR which is used in the
context of copy elision.  If I have

  M m[2] = { M{""}, M{""} };

then we don't invoke the M(const M&) copy-ctor.

One part of the fix is to use TARGET_EXPR_ELIDING_P rather than
potential_prvalue_result_of.  That unfortunately doesn't handle the
case like

  struct N { N(M); };
  N arr[2] = { M{""}, M{""} };

because TARGET_EXPRs that initialize a function argument are not
marked TARGET_EXPR_ELIDING_P even though gimplify_arg drops such
TARGET_EXPRs on the floor.  We can use a pset to avoid replacing
placeholders in them.

I made an attempt to use set_target_expr_eliding in
convert_for_arg_passing but that regressed constexpr-diag1.C, and does
not seem like a prudent change in stage 4 anyway.

	PR c++/109966

gcc/cp/ChangeLog:

	* typeck2.cc (potential_prvalue_result_of): Remove.
	(replace_placeholders_for_class_temp_r): Check TARGET_EXPR_ELIDING_P.
	Use a pset.  Don't replace_placeholders in TARGET_EXPRs that initialize
	a function argument.

gcc/testsuite/ChangeLog:

	* g++.dg/cpp1y/nsdmi-aggr20.C: New test.
	* g++.dg/cpp1y/nsdmi-aggr21.C: New test.
---
 gcc/cp/typeck2.cc                         | 55 ++++++---------------
 gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr20.C | 17 +++++++
 gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr21.C | 59 +++++++++++++++++++++++
 3 files changed, 92 insertions(+), 39 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr20.C
 create mode 100644 gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr21.C

diff --git a/gcc/cp/typeck2.cc b/gcc/cp/typeck2.cc
index 31198b2f9f5..4bf3201eedc 100644
--- a/gcc/cp/typeck2.cc
+++ b/gcc/cp/typeck2.cc
@@ -1399,41 +1399,6 @@ digest_init_flags (tree type, tree init, int flags, tsubst_flags_t complain)
   return digest_init_r (type, init, 0, flags, complain);
 }
 
-/* Return true if SUBOB initializes the same object as FULL_EXPR.
-   For instance:
-
-     A a = A{};		      // initializer
-     A a = (A{});	      // initializer
-     A a = (1, A{});	      // initializer
-     A a = true ? A{} : A{};  // initializer
-     auto x = A{}.x;	      // temporary materialization
-     auto x = foo(A{});	      // temporary materialization
-
-   FULL_EXPR is the whole expression, SUBOB is its TARGET_EXPR subobject.  */
-
-static bool
-potential_prvalue_result_of (tree subob, tree full_expr)
-{
-  if (subob == full_expr)
-    return true;
-  else if (TREE_CODE (full_expr) == TARGET_EXPR)
-    {
-      tree init = TARGET_EXPR_INITIAL (full_expr);
-      if (TREE_CODE (init) == COND_EXPR)
-	return (potential_prvalue_result_of (subob, TREE_OPERAND (init, 1))
-		|| potential_prvalue_result_of (subob, TREE_OPERAND (init, 2)));
-      else if (TREE_CODE (init) == COMPOUND_EXPR)
-	return potential_prvalue_result_of (subob, TREE_OPERAND (init, 1));
-      /* ??? I don't know if this can be hit.  */
-      else if (TREE_CODE (init) == PAREN_EXPR)
-	{
-	  gcc_checking_assert (false);
-	  return potential_prvalue_result_of (subob, TREE_OPERAND (init, 0));
-	}
-    }
-  return false;
-}
-
 /* Callback to replace PLACEHOLDER_EXPRs in a TARGET_EXPR (which isn't used
    in the context of guaranteed copy elision).  */
 
@@ -1441,11 +1406,13 @@ static tree
 replace_placeholders_for_class_temp_r (tree *tp, int *, void *data)
 {
   tree t = *tp;
-  tree full_expr = *static_cast<tree *>(data);
+  auto pset = static_cast<hash_set<tree> *>(data);
 
   /* We're looking for a TARGET_EXPR nested in the whole expression.  */
   if (TREE_CODE (t) == TARGET_EXPR
-      && !potential_prvalue_result_of (t, full_expr))
+      /* That serves as temporary materialization, not an initializer.  */
+      && !TARGET_EXPR_ELIDING_P (t)
+      && !pset->add (t))
     {
       tree init = TARGET_EXPR_INITIAL (t);
       while (TREE_CODE (init) == COMPOUND_EXPR)
@@ -1460,6 +1427,16 @@ replace_placeholders_for_class_temp_r (tree *tp, int *, void *data)
 	  gcc_checking_assert (!find_placeholders (init));
 	}
     }
+  /* TARGET_EXPRs initializing function arguments are not marked as eliding,
+     even though gimplify_arg drops them on the floor.  Don't go replacing
+     placeholders in them.  */
+  else 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)
+	  pset->add (arg);
+      }
 
   return NULL_TREE;
 }
@@ -1507,8 +1484,8 @@ digest_nsdmi_init (tree decl, tree init, tsubst_flags_t complain)
      temporary materialization does not occur when initializing an object
      from a prvalue of the same type, therefore we must not replace the
      placeholder with a temporary object so that it can be elided.  */
-  cp_walk_tree (&init, replace_placeholders_for_class_temp_r, &init,
-		nullptr);
+  hash_set<tree> pset;
+  cp_walk_tree (&init, replace_placeholders_for_class_temp_r, &pset, nullptr);
 
   return init;
 }
diff --git a/gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr20.C b/gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr20.C
new file mode 100644
index 00000000000..4796d861e83
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr20.C
@@ -0,0 +1,17 @@
+// PR c++/109966
+// { dg-do compile { target c++14 } }
+
+#define SA(X) static_assert ((X),#X)
+
+struct A {
+  int a;
+  int b = a;
+};
+
+struct B {
+  int x = 0;
+  int y[1]{A{x}.b};
+};
+
+constexpr B b = { };
+SA(b.y[0] == 0);
diff --git a/gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr21.C b/gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr21.C
new file mode 100644
index 00000000000..efec45bc1a8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr21.C
@@ -0,0 +1,59 @@
+// PR c++/109966
+// { dg-do compile { target c++14 } }
+
+struct k {
+  k(const char *);
+};
+struct M {
+  k name;
+  int j = 42;
+  int i = j;
+};
+
+M m = M{""};
+
+struct S {
+  M arr1[1]{M{""}};
+  M a1[1] = { (M{""}) };
+  M a2[1] = { (true, M{""}) };
+  M a3[1] = { true ? M{""} : M{""} };
+  M arr2[2]{M{""}, M{""}};
+  M arr3[3]{M{""}, M{""}, M{""}};
+
+  M arr1e[1] = {M{""}};
+  M arr2e[2] = {M{""}, M{""}};
+  M arr3e[3] = {M{""}, M{""}, M{""}};
+
+  M arr1l[1] = { m };
+  M arr2l[2] = { m, m };
+  M arr3l[3] = { m, m, m };
+
+  M m1 = M{""};
+  M m2 = m;
+  M m3{M{""}};
+  M m4 = {M{""}};
+} o;
+
+struct N {
+  N(M);
+};
+
+struct Z {
+  N arr1[1]{ M{""} };
+  N arr2[2]{ M{""}, M{""} };
+  N arr1e[1] = { M{""} };
+  N arr2e[2] = { M{""}, M{""} };
+} z;
+
+struct Y {
+  k name;
+  int j = 42;
+  int i = j;
+  operator M();
+};
+
+struct W {
+  M arr1[1]{ Y{""} };
+  M arr2[2]{ Y{""}, Y{""} };
+  M arr3[3]{ Y{""}, Y{""}, Y{""} };
+} w;

base-commit: 6dbf0d252f69ab2924256e6778ba7dc55d5b6915
-- 
2.44.0


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v3] c++: ICE with temporary of class type in array DMI [PR109966]
  2024-03-14 21:26       ` [PATCH v3] " Marek Polacek
@ 2024-03-19 19:47         ` Marek Polacek
  2024-04-12 20:15         ` Jason Merrill
  1 sibling, 0 replies; 8+ messages in thread
From: Marek Polacek @ 2024-03-19 19:47 UTC (permalink / raw)
  To: Jason Merrill; +Cc: GCC Patches

On Thu, Mar 14, 2024 at 05:26:59PM -0400, Marek Polacek wrote:
> @@ -1441,11 +1406,13 @@ static tree
>  replace_placeholders_for_class_temp_r (tree *tp, int *, void *data)
>  {
>    tree t = *tp;
> -  tree full_expr = *static_cast<tree *>(data);
> +  auto pset = static_cast<hash_set<tree> *>(data);
>  
>    /* We're looking for a TARGET_EXPR nested in the whole expression.  */
>    if (TREE_CODE (t) == TARGET_EXPR
> -      && !potential_prvalue_result_of (t, full_expr))
> +      /* That serves as temporary materialization, not an initializer.  */
> +      && !TARGET_EXPR_ELIDING_P (t)
> +      && !pset->add (t))
>      {
>        tree init = TARGET_EXPR_INITIAL (t);
>        while (TREE_CODE (init) == COMPOUND_EXPR)
> @@ -1460,6 +1427,16 @@ replace_placeholders_for_class_temp_r (tree *tp, int *, void *data)
>  	  gcc_checking_assert (!find_placeholders (init));
>  	}
>      }
> +  /* TARGET_EXPRs initializing function arguments are not marked as eliding,
> +     even though gimplify_arg drops them on the floor.  Don't go replacing
> +     placeholders in them.  */
> +  else 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)

I just realized this could also check !TARGET_EXPR_ELIDING_P; there's no point
to adding an eliding TARGET_EXPR into the pset.

> +	  pset->add (arg);
> +      }

Marek


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v3] c++: ICE with temporary of class type in array DMI [PR109966]
  2024-03-14 21:26       ` [PATCH v3] " Marek Polacek
  2024-03-19 19:47         ` Marek Polacek
@ 2024-04-12 20:15         ` Jason Merrill
  2024-04-12 21:41           ` Marek Polacek
  1 sibling, 1 reply; 8+ messages in thread
From: Jason Merrill @ 2024-04-12 20:15 UTC (permalink / raw)
  To: Marek Polacek; +Cc: GCC Patches

On 3/14/24 17:26, Marek Polacek wrote:
> 
> In the following patch, I'm taking a different tack.  I believe
> we ought to use TARGET_EXPR_ELIDING_P.  The gimplify_arg bit I'm
> talking about below is this:
> 
>        /* Also strip a TARGET_EXPR that would force an extra copy.  */
>        if (TREE_CODE (*arg_p) == TARGET_EXPR)
>          {
>            tree init = TARGET_EXPR_INITIAL (*arg_p);
>            if (init
>                && !VOID_TYPE_P (TREE_TYPE (init)))
>              *arg_p = init;
>          }
> 
> Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk/13?
> 
> -- >8 --
> This ICE started with the fairly complicated r13-765.  We crash in
> gimplify_var_or_parm_decl because a stray VAR_DECL leaked there.
> The problem is ultimately that potential_prvalue_result_of wasn't
> correctly handling arrays and replace_placeholders_for_class_temp_r
> replaced a PLACEHOLDER_EXPR in a TARGET_EXPR which is used in the
> context of copy elision.  If I have
> 
>    M m[2] = { M{""}, M{""} };
> 
> then we don't invoke the M(const M&) copy-ctor.
> 
> One part of the fix is to use TARGET_EXPR_ELIDING_P rather than
> potential_prvalue_result_of.  That unfortunately doesn't handle the
> case like
> 
>    struct N { N(M); };
>    N arr[2] = { M{""}, M{""} };
> 
> because TARGET_EXPRs that initialize a function argument are not
> marked TARGET_EXPR_ELIDING_P even though gimplify_arg drops such
> TARGET_EXPRs on the floor.  We can use a pset to avoid replacing
> placeholders in them.
> 
> I made an attempt to use set_target_expr_eliding in
> convert_for_arg_passing but that regressed constexpr-diag1.C, and does
> not seem like a prudent change in stage 4 anyway.

I tried the same thing to see what you mean, and that doesn't look like 
a regression to me, just a different (and more accurate) diagnostic.

But you're right that this patch is safer, and the other approach can 
wait for stage 1.  Will you queue that up?  In the mean time, this patch 
is OK.

> I just realized this could also check !TARGET_EXPR_ELIDING_P; there's no point
> to adding an eliding TARGET_EXPR into the pset.

...with this change.

Jason


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v3] c++: ICE with temporary of class type in array DMI [PR109966]
  2024-04-12 20:15         ` Jason Merrill
@ 2024-04-12 21:41           ` Marek Polacek
  0 siblings, 0 replies; 8+ messages in thread
From: Marek Polacek @ 2024-04-12 21:41 UTC (permalink / raw)
  To: Jason Merrill; +Cc: GCC Patches

On Fri, Apr 12, 2024 at 04:15:45PM -0400, Jason Merrill wrote:
> On 3/14/24 17:26, Marek Polacek wrote:
> > 
> > In the following patch, I'm taking a different tack.  I believe
> > we ought to use TARGET_EXPR_ELIDING_P.  The gimplify_arg bit I'm
> > talking about below is this:
> > 
> >        /* Also strip a TARGET_EXPR that would force an extra copy.  */
> >        if (TREE_CODE (*arg_p) == TARGET_EXPR)
> >          {
> >            tree init = TARGET_EXPR_INITIAL (*arg_p);
> >            if (init
> >                && !VOID_TYPE_P (TREE_TYPE (init)))
> >              *arg_p = init;
> >          }
> > 
> > Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk/13?
> > 
> > -- >8 --
> > This ICE started with the fairly complicated r13-765.  We crash in
> > gimplify_var_or_parm_decl because a stray VAR_DECL leaked there.
> > The problem is ultimately that potential_prvalue_result_of wasn't
> > correctly handling arrays and replace_placeholders_for_class_temp_r
> > replaced a PLACEHOLDER_EXPR in a TARGET_EXPR which is used in the
> > context of copy elision.  If I have
> > 
> >    M m[2] = { M{""}, M{""} };
> > 
> > then we don't invoke the M(const M&) copy-ctor.
> > 
> > One part of the fix is to use TARGET_EXPR_ELIDING_P rather than
> > potential_prvalue_result_of.  That unfortunately doesn't handle the
> > case like
> > 
> >    struct N { N(M); };
> >    N arr[2] = { M{""}, M{""} };
> > 
> > because TARGET_EXPRs that initialize a function argument are not
> > marked TARGET_EXPR_ELIDING_P even though gimplify_arg drops such
> > TARGET_EXPRs on the floor.  We can use a pset to avoid replacing
> > placeholders in them.
> > 
> > I made an attempt to use set_target_expr_eliding in
> > convert_for_arg_passing but that regressed constexpr-diag1.C, and does
> > not seem like a prudent change in stage 4 anyway.
> 
> I tried the same thing to see what you mean, and that doesn't look like a
> regression to me, just a different (and more accurate) diagnostic.
> 
> But you're right that this patch is safer, and the other approach can wait
> for stage 1.  Will you queue that up?  In the mean time, this patch is OK.

Yeah, happy to; I've opened 114707 to remember.
 
> > I just realized this could also check !TARGET_EXPR_ELIDING_P; there's no point
> > to adding an eliding TARGET_EXPR into the pset.
> 
> ...with this change.

Thanks.

Marek


^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2024-04-12 21:41 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-03-11 23:27 [PATCH] c++: ICE with temporary of class type in array DMI [PR109966] Marek Polacek
2024-03-12 13:57 ` Jason Merrill
2024-03-12 15:56   ` [PATCH v2] " Marek Polacek
2024-03-12 22:26     ` Jason Merrill
2024-03-14 21:26       ` [PATCH v3] " Marek Polacek
2024-03-19 19:47         ` Marek Polacek
2024-04-12 20:15         ` Jason Merrill
2024-04-12 21:41           ` Marek Polacek

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).