c++: Handle structured bindings like anon unions in initializers [PR108474]

Message ID Y8u3h8B7//8hKdHh@tucnak
State New
Headers
Series c++: Handle structured bindings like anon unions in initializers [PR108474] |

Commit Message

Jakub Jelinek Jan. 21, 2023, 9:59 a.m. UTC
  Hi!

As reported by Andrew Pinski, structured bindings (with the exception
of the ones using std::tuple_{size,element} and get which are really
standalone variables in addition to the binding one) also use
DECL_VALUE_EXPR and needs the same treatment in static initializers.

Fixed thusly, bootstrapped/regtested on x86_64-linux and i686-linux, ok
for trunk?

2023-01-21  Jakub Jelinek  <jakub@redhat.com>

	PR c++/108474
	* cp-gimplify.cc (cp_fold_r): Handle structured bindings
	vars like anon union artificial vars.

	* g++.dg/cpp1z/decomp57.C: New test.
	* g++.dg/cpp1z/decomp58.C: New test.


	Jakub
  

Comments

Jason Merrill Jan. 22, 2023, 8:40 p.m. UTC | #1
On 1/21/23 04:59, Jakub Jelinek wrote:
> Hi!
> 
> As reported by Andrew Pinski, structured bindings (with the exception
> of the ones using std::tuple_{size,element} and get which are really
> standalone variables in addition to the binding one) also use
> DECL_VALUE_EXPR and needs the same treatment in static initializers.
> 
> Fixed thusly, bootstrapped/regtested on x86_64-linux and i686-linux, ok
> for trunk?
> 
> 2023-01-21  Jakub Jelinek  <jakub@redhat.com>
> 
> 	PR c++/108474
> 	* cp-gimplify.cc (cp_fold_r): Handle structured bindings
> 	vars like anon union artificial vars.
> 
> 	* g++.dg/cpp1z/decomp57.C: New test.
> 	* g++.dg/cpp1z/decomp58.C: New test.
> 
> --- gcc/cp/cp-gimplify.cc.jj	2023-01-19 23:27:27.998400866 +0100
> +++ gcc/cp/cp-gimplify.cc	2023-01-20 11:00:06.093446737 +0100
> @@ -1012,8 +1012,12 @@ cp_fold_r (tree *stmt_p, int *walk_subtr
>   
>       case VAR_DECL:
>         /* In initializers replace anon union artificial VAR_DECLs
> -	 with their DECL_VALUE_EXPRs, as nothing will do it later.  */
> -      if (DECL_ANON_UNION_VAR_P (stmt) && !data->genericize)
> +	 with their DECL_VALUE_EXPRs, as nothing will do it later.
> +	 Ditto for structured bindings.  */
> +      if (!data->genericize
> +	  && DECL_HAS_VALUE_EXPR_P (stmt)
> +	  && (DECL_ANON_UNION_VAR_P (stmt)
> +	      || (DECL_DECOMPOSITION_P (stmt) && DECL_DECOMP_BASE (stmt))))

Is there a reason not to do this for all cases of DECL_HAS_VALUE_EXPR_P, 
without the extra checks?

>   	{
>   	  *stmt_p = stmt = unshare_expr (DECL_VALUE_EXPR (stmt));
>   	  break;
> --- gcc/testsuite/g++.dg/cpp1z/decomp57.C.jj	2023-01-20 11:02:08.547656638 +0100
> +++ gcc/testsuite/g++.dg/cpp1z/decomp57.C	2023-01-20 10:53:01.781649565 +0100
> @@ -0,0 +1,27 @@
> +// PR c++/108474
> +// { dg-do link { target c++17 } }
> +
> +struct T { int i, j; };
> +T h;
> +auto [i, j] = h;
> +int &r = i;
> +int s = i;
> +int *t = &i;
> +
> +void
> +foo (int **p, int *q)
> +{
> +  static int &u = i;
> +  static int v = i;
> +  static int *w = &i;
> +  int &x = i;
> +  int y = i;
> +  int *z = &i;
> +  *p = &i;
> +  *q = i;
> +}
> +
> +int
> +main ()
> +{
> +}
> --- gcc/testsuite/g++.dg/cpp1z/decomp58.C.jj	2023-01-20 11:02:12.314601575 +0100
> +++ gcc/testsuite/g++.dg/cpp1z/decomp58.C	2023-01-20 10:54:02.117767542 +0100
> @@ -0,0 +1,39 @@
> +// PR c++/108474
> +// { dg-do link { target c++17 } }
> +
> +namespace std {
> +  template <typename T> struct tuple_size;
> +  template <int, typename> struct tuple_element;
> +}
> +
> +struct A {
> +  int i;
> +  template <int I> int& get() { return i; }
> +};
> +
> +template <> struct std::tuple_size <A> { static const int value = 2; };
> +template <int I> struct std::tuple_element <I, A> { using type = int; };
> +
> +struct A a;
> +auto [i, j] = a;
> +int &r = i;
> +int s = i;
> +int *t = &i;
> +
> +void
> +foo (int **p, int *q)
> +{
> +  static int &u = i;
> +  static int v = i;
> +  static int *w = &i;
> +  int &x = i;
> +  int y = i;
> +  int *z = &i;
> +  *p = &i;
> +  *q = i;
> +}
> +
> +int
> +main ()
> +{
> +}
> 
> 	Jakub
>
  
Jakub Jelinek Jan. 22, 2023, 8:50 p.m. UTC | #2
On Sun, Jan 22, 2023 at 03:40:26PM -0500, Jason Merrill wrote:
> > 2023-01-21  Jakub Jelinek  <jakub@redhat.com>
> > 
> > 	PR c++/108474
> > 	* cp-gimplify.cc (cp_fold_r): Handle structured bindings
> > 	vars like anon union artificial vars.
> > 
> > 	* g++.dg/cpp1z/decomp57.C: New test.
> > 	* g++.dg/cpp1z/decomp58.C: New test.
> > 
> > --- gcc/cp/cp-gimplify.cc.jj	2023-01-19 23:27:27.998400866 +0100
> > +++ gcc/cp/cp-gimplify.cc	2023-01-20 11:00:06.093446737 +0100
> > @@ -1012,8 +1012,12 @@ cp_fold_r (tree *stmt_p, int *walk_subtr
> >       case VAR_DECL:
> >         /* In initializers replace anon union artificial VAR_DECLs
> > -	 with their DECL_VALUE_EXPRs, as nothing will do it later.  */
> > -      if (DECL_ANON_UNION_VAR_P (stmt) && !data->genericize)
> > +	 with their DECL_VALUE_EXPRs, as nothing will do it later.
> > +	 Ditto for structured bindings.  */
> > +      if (!data->genericize
> > +	  && DECL_HAS_VALUE_EXPR_P (stmt)
> > +	  && (DECL_ANON_UNION_VAR_P (stmt)
> > +	      || (DECL_DECOMPOSITION_P (stmt) && DECL_DECOMP_BASE (stmt))))
> 
> Is there a reason not to do this for all cases of DECL_HAS_VALUE_EXPR_P,
> without the extra checks?

I was just trying to be careful, because unfortunately this spot
doesn't mean it really is only expanded in static var DECL_INITIAL,
it can make it into dynamic initializers, and most of DECL_VALUE_EXPRs
appear only in runtime code, otherwise we'd have much more of these issues.

But if you think it is ok, I'll test tonight a version just with
if (!data->genericize && DECL_HAS_VALUE_EXPR_P (stmt)

	Jakub
  
Jason Merrill Jan. 23, 2023, 12:19 a.m. UTC | #3
On 1/22/23 15:50, Jakub Jelinek wrote:
> On Sun, Jan 22, 2023 at 03:40:26PM -0500, Jason Merrill wrote:
>>> 2023-01-21  Jakub Jelinek  <jakub@redhat.com>
>>>
>>> 	PR c++/108474
>>> 	* cp-gimplify.cc (cp_fold_r): Handle structured bindings
>>> 	vars like anon union artificial vars.
>>>
>>> 	* g++.dg/cpp1z/decomp57.C: New test.
>>> 	* g++.dg/cpp1z/decomp58.C: New test.
>>>
>>> --- gcc/cp/cp-gimplify.cc.jj	2023-01-19 23:27:27.998400866 +0100
>>> +++ gcc/cp/cp-gimplify.cc	2023-01-20 11:00:06.093446737 +0100
>>> @@ -1012,8 +1012,12 @@ cp_fold_r (tree *stmt_p, int *walk_subtr
>>>        case VAR_DECL:
>>>          /* In initializers replace anon union artificial VAR_DECLs
>>> -	 with their DECL_VALUE_EXPRs, as nothing will do it later.  */
>>> -      if (DECL_ANON_UNION_VAR_P (stmt) && !data->genericize)
>>> +	 with their DECL_VALUE_EXPRs, as nothing will do it later.
>>> +	 Ditto for structured bindings.  */
>>> +      if (!data->genericize
>>> +	  && DECL_HAS_VALUE_EXPR_P (stmt)
>>> +	  && (DECL_ANON_UNION_VAR_P (stmt)
>>> +	      || (DECL_DECOMPOSITION_P (stmt) && DECL_DECOMP_BASE (stmt))))
>>
>> Is there a reason not to do this for all cases of DECL_HAS_VALUE_EXPR_P,
>> without the extra checks?
> 
> I was just trying to be careful, because unfortunately this spot
> doesn't mean it really is only expanded in static var DECL_INITIAL,
> it can make it into dynamic initializers, and most of DECL_VALUE_EXPRs
> appear only in runtime code, otherwise we'd have much more of these issues.

But it shouldn't be harmful anywhere, right?

> But if you think it is ok, I'll test tonight a version just with
> if (!data->genericize && DECL_HAS_VALUE_EXPR_P (stmt)

OK with that change.

Though, actually, why not instead fix expand_expr_real_1 (and staticp) 
to look through DECL_VALUE_EXPR?

Jason
  
Jakub Jelinek Jan. 23, 2023, 8:25 a.m. UTC | #4
On Sun, Jan 22, 2023 at 07:19:07PM -0500, Jason Merrill wrote:
> > I was just trying to be careful, because unfortunately this spot
> > doesn't mean it really is only expanded in static var DECL_INITIAL,
> > it can make it into dynamic initializers, and most of DECL_VALUE_EXPRs
> > appear only in runtime code, otherwise we'd have much more of these issues.
> 
> But it shouldn't be harmful anywhere, right?
> 
> > But if you think it is ok, I'll test tonight a version just with
> > if (!data->genericize && DECL_HAS_VALUE_EXPR_P (stmt)
> 
> OK with that change.
> 
> Though, actually, why not instead fix expand_expr_real_1 (and staticp) to
> look through DECL_VALUE_EXPR?

I'm afraid that is way too late.  GIMPLE optimizations don't try to
regimplify expressions they take from DECL_INITIAL of static vars, they
just unshare them and use them.  So, if this is to be done in the generic
code, it would need to be done by cgraph around the time when it gimplifies
function if there is any such point for variables.

	Jakub
  

Patch

--- gcc/cp/cp-gimplify.cc.jj	2023-01-19 23:27:27.998400866 +0100
+++ gcc/cp/cp-gimplify.cc	2023-01-20 11:00:06.093446737 +0100
@@ -1012,8 +1012,12 @@  cp_fold_r (tree *stmt_p, int *walk_subtr
 
     case VAR_DECL:
       /* In initializers replace anon union artificial VAR_DECLs
-	 with their DECL_VALUE_EXPRs, as nothing will do it later.  */
-      if (DECL_ANON_UNION_VAR_P (stmt) && !data->genericize)
+	 with their DECL_VALUE_EXPRs, as nothing will do it later.
+	 Ditto for structured bindings.  */
+      if (!data->genericize
+	  && DECL_HAS_VALUE_EXPR_P (stmt)
+	  && (DECL_ANON_UNION_VAR_P (stmt)
+	      || (DECL_DECOMPOSITION_P (stmt) && DECL_DECOMP_BASE (stmt))))
 	{
 	  *stmt_p = stmt = unshare_expr (DECL_VALUE_EXPR (stmt));
 	  break;
--- gcc/testsuite/g++.dg/cpp1z/decomp57.C.jj	2023-01-20 11:02:08.547656638 +0100
+++ gcc/testsuite/g++.dg/cpp1z/decomp57.C	2023-01-20 10:53:01.781649565 +0100
@@ -0,0 +1,27 @@ 
+// PR c++/108474
+// { dg-do link { target c++17 } }
+
+struct T { int i, j; };
+T h;
+auto [i, j] = h;
+int &r = i;
+int s = i;
+int *t = &i;
+
+void
+foo (int **p, int *q)
+{
+  static int &u = i;
+  static int v = i;
+  static int *w = &i;
+  int &x = i;
+  int y = i;
+  int *z = &i;
+  *p = &i;
+  *q = i;
+}
+
+int
+main ()
+{
+}
--- gcc/testsuite/g++.dg/cpp1z/decomp58.C.jj	2023-01-20 11:02:12.314601575 +0100
+++ gcc/testsuite/g++.dg/cpp1z/decomp58.C	2023-01-20 10:54:02.117767542 +0100
@@ -0,0 +1,39 @@ 
+// PR c++/108474
+// { dg-do link { target c++17 } }
+
+namespace std {
+  template <typename T> struct tuple_size;
+  template <int, typename> struct tuple_element;
+}
+
+struct A {
+  int i;
+  template <int I> int& get() { return i; }
+};
+
+template <> struct std::tuple_size <A> { static const int value = 2; };
+template <int I> struct std::tuple_element <I, A> { using type = int; };
+
+struct A a;
+auto [i, j] = a;
+int &r = i;
+int s = i;
+int *t = &i;
+
+void
+foo (int **p, int *q)
+{
+  static int &u = i;
+  static int v = i;
+  static int *w = &i;
+  int &x = i;
+  int y = i;
+  int *z = &i;
+  *p = &i;
+  *q = i;
+}
+
+int
+main ()
+{
+}