c++, cgraphbuild: Handle DECL_VALUE_EXPRs in record_reference [PR108474]

Message ID Y85shC6wzmxApdZM@tucnak
State New
Headers
Series c++, cgraphbuild: Handle DECL_VALUE_EXPRs in record_reference [PR108474] |

Commit Message

Jakub Jelinek Jan. 23, 2023, 11:16 a.m. UTC
  On Mon, Jan 23, 2023 at 09:25:50AM +0100, Jakub Jelinek via Gcc-patches wrote:
> 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.

I believe the right spot is record_reference, which is called through
walk_tree from record_references_in_initializer which is called from
varpool_node::analyze.

The new tests as well as g++.dg/init/pr53932.C pass with it, will do full
bootstrap/regtest soon.

What do you think about this?

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

	PR c++/108474
	* cgraphbuild.cc: Include gimplify.h.
	(record_reference): Replace VAR_DECLs with DECL_HAS_VALUE_EXPR_P with
	their corresponding DECL_VALUE_EXPR expressions after unsharing.

	* cp-gimplify.cc (cp_fold_r): Revert 2023-01-19 changes.

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



	Jakub
  

Comments

Richard Biener Jan. 23, 2023, 12:37 p.m. UTC | #1
On Mon, 23 Jan 2023, Jakub Jelinek wrote:

> On Mon, Jan 23, 2023 at 09:25:50AM +0100, Jakub Jelinek via Gcc-patches wrote:
> > 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.
> 
> I believe the right spot is record_reference, which is called through
> walk_tree from record_references_in_initializer which is called from
> varpool_node::analyze.
> 
> The new tests as well as g++.dg/init/pr53932.C pass with it, will do full
> bootstrap/regtest soon.
> 
> What do you think about this?

Guess it should work.  Do we (accidentially?) do anything special
to static var initializers in nested functions?  Can you think of
any other C/C++ construct that would have DECL_VALUE_EXPR in them?

Richard.

> 2023-01-23  Jakub Jelinek  <jakub@redhat.com>
> 
> 	PR c++/108474
> 	* cgraphbuild.cc: Include gimplify.h.
> 	(record_reference): Replace VAR_DECLs with DECL_HAS_VALUE_EXPR_P with
> 	their corresponding DECL_VALUE_EXPR expressions after unsharing.
> 
> 	* cp-gimplify.cc (cp_fold_r): Revert 2023-01-19 changes.
> 
> 	* g++.dg/cpp1z/decomp57.C: New test.
> 	* g++.dg/cpp1z/decomp58.C: New test.
> 
> --- gcc/cgraphbuild.cc.jj	2023-01-02 09:32:34.889104620 +0100
> +++ gcc/cgraphbuild.cc	2023-01-23 12:10:35.042067571 +0100
> @@ -31,6 +31,7 @@ along with GCC; see the file COPYING3.
>  #include "gimple-walk.h"
>  #include "ipa-utils.h"
>  #include "except.h"
> +#include "gimplify.h"
>  
>  /* Context of record_reference.  */
>  struct record_reference_ctx
> @@ -79,6 +80,17 @@ record_reference (tree *tp, int *walk_su
>  
>        if (VAR_P (decl))
>  	{
> +	  /* Replace vars with their DECL_VALUE_EXPR if any.
> +	     This is normally done during gimplification, but
> +	     static var initializers are never gimplified.  */
> +	  if (DECL_HAS_VALUE_EXPR_P (decl))
> +	    {
> +	      tree *p;
> +	      for (p = tp; *p != decl; p = &TREE_OPERAND (*p, 0))
> +		;
> +	      *p = unshare_expr (DECL_VALUE_EXPR (decl));
> +	      return record_reference (tp, walk_subtrees, data);
> +	    }
>  	  varpool_node *vnode = varpool_node::get_create (decl);
>  	  ctx->varpool_node->create_reference (vnode, IPA_REF_ADDR);
>  	}
> --- gcc/cp/cp-gimplify.cc.jj	2023-01-23 11:47:49.889875804 +0100
> +++ gcc/cp/cp-gimplify.cc	2023-01-23 11:49:01.227841759 +0100
> @@ -1010,16 +1010,6 @@ cp_fold_r (tree *stmt_p, int *walk_subtr
>  	}
>        break;
>  
> -    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)
> -	{
> -	  *stmt_p = stmt = unshare_expr (DECL_VALUE_EXPR (stmt));
> -	  break;
> -	}
> -      break;
> -
>      default:
>        break;
>      }
> --- gcc/testsuite/g++.dg/cpp1z/decomp57.C.jj	2023-01-23 11:48:36.367202107 +0100
> +++ gcc/testsuite/g++.dg/cpp1z/decomp57.C	2023-01-23 11:48:36.367202107 +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-23 11:48:36.367202107 +0100
> +++ gcc/testsuite/g++.dg/cpp1z/decomp58.C	2023-01-23 11:48:36.367202107 +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. 23, 2023, 3:17 p.m. UTC | #2
On Mon, Jan 23, 2023 at 12:37:59PM +0000, Richard Biener wrote:
> Guess it should work.  Do we (accidentially?) do anything special
> to static var initializers in nested functions?

I don't think we touch those, we walk the bodies of functions, I don't
think we traverse static var DECL_INITIALs.

>  Can you think of
> any other C/C++ construct that would have DECL_VALUE_EXPR in them?

A lot of them, coroutines, FE NRV, anon union vars, lambdas,
in modules, OpenMP privatized members, structured bindings,
__FUNCTION__ etc., later VLAs, tree-nested, OpenMP lowering.

But anon union vars and structured bindings are the only ones
known for which this is actually needed in static initializers.
I'm afraid no idea about modules case, for OpenMP one needs executable
code, tried the __FUNCTION__ case and while there is DECL_VALUE_EXPR
used, the initializer is already replaced by STRING_CST in the FE somewhere,
VLAs aren't allowed.

BTW, the patch successfully passed bootstrap/regtested on x86_64-linux and
i686-linux.

> > 2023-01-23  Jakub Jelinek  <jakub@redhat.com>
> > 
> > 	PR c++/108474
> > 	* cgraphbuild.cc: Include gimplify.h.
> > 	(record_reference): Replace VAR_DECLs with DECL_HAS_VALUE_EXPR_P with
> > 	their corresponding DECL_VALUE_EXPR expressions after unsharing.
> > 
> > 	* cp-gimplify.cc (cp_fold_r): Revert 2023-01-19 changes.
> > 
> > 	* g++.dg/cpp1z/decomp57.C: New test.
> > 	* g++.dg/cpp1z/decomp58.C: New test.
> > 
> > --- gcc/cgraphbuild.cc.jj	2023-01-02 09:32:34.889104620 +0100
> > +++ gcc/cgraphbuild.cc	2023-01-23 12:10:35.042067571 +0100
> > @@ -31,6 +31,7 @@ along with GCC; see the file COPYING3.
> >  #include "gimple-walk.h"
> >  #include "ipa-utils.h"
> >  #include "except.h"
> > +#include "gimplify.h"
> >  
> >  /* Context of record_reference.  */
> >  struct record_reference_ctx
> > @@ -79,6 +80,17 @@ record_reference (tree *tp, int *walk_su
> >  
> >        if (VAR_P (decl))
> >  	{
> > +	  /* Replace vars with their DECL_VALUE_EXPR if any.
> > +	     This is normally done during gimplification, but
> > +	     static var initializers are never gimplified.  */
> > +	  if (DECL_HAS_VALUE_EXPR_P (decl))
> > +	    {
> > +	      tree *p;
> > +	      for (p = tp; *p != decl; p = &TREE_OPERAND (*p, 0))
> > +		;
> > +	      *p = unshare_expr (DECL_VALUE_EXPR (decl));
> > +	      return record_reference (tp, walk_subtrees, data);
> > +	    }
> >  	  varpool_node *vnode = varpool_node::get_create (decl);
> >  	  ctx->varpool_node->create_reference (vnode, IPA_REF_ADDR);
> >  	}
> > --- gcc/cp/cp-gimplify.cc.jj	2023-01-23 11:47:49.889875804 +0100
> > +++ gcc/cp/cp-gimplify.cc	2023-01-23 11:49:01.227841759 +0100
> > @@ -1010,16 +1010,6 @@ cp_fold_r (tree *stmt_p, int *walk_subtr
> >  	}
> >        break;
> >  
> > -    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)
> > -	{
> > -	  *stmt_p = stmt = unshare_expr (DECL_VALUE_EXPR (stmt));
> > -	  break;
> > -	}
> > -      break;
> > -
> >      default:
> >        break;
> >      }
> > --- gcc/testsuite/g++.dg/cpp1z/decomp57.C.jj	2023-01-23 11:48:36.367202107 +0100
> > +++ gcc/testsuite/g++.dg/cpp1z/decomp57.C	2023-01-23 11:48:36.367202107 +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-23 11:48:36.367202107 +0100
> > +++ gcc/testsuite/g++.dg/cpp1z/decomp58.C	2023-01-23 11:48:36.367202107 +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
  
Richard Biener Jan. 23, 2023, 3:22 p.m. UTC | #3
On Mon, 23 Jan 2023, Jakub Jelinek wrote:

> On Mon, Jan 23, 2023 at 12:37:59PM +0000, Richard Biener wrote:
> > Guess it should work.  Do we (accidentially?) do anything special
> > to static var initializers in nested functions?
> 
> I don't think we touch those, we walk the bodies of functions, I don't
> think we traverse static var DECL_INITIALs.
> 
> >  Can you think of
> > any other C/C++ construct that would have DECL_VALUE_EXPR in them?
> 
> A lot of them, coroutines, FE NRV, anon union vars, lambdas,
> in modules, OpenMP privatized members, structured bindings,
> __FUNCTION__ etc., later VLAs, tree-nested, OpenMP lowering.
> 
> But anon union vars and structured bindings are the only ones
> known for which this is actually needed in static initializers.
> I'm afraid no idea about modules case, for OpenMP one needs executable
> code, tried the __FUNCTION__ case and while there is DECL_VALUE_EXPR
> used, the initializer is already replaced by STRING_CST in the FE somewhere,
> VLAs aren't allowed.
> 
> BTW, the patch successfully passed bootstrap/regtested on x86_64-linux and
> i686-linux.

So let's try and revert quickly if any odd fallout appears?

Richard.

> 
> > > 2023-01-23  Jakub Jelinek  <jakub@redhat.com>
> > > 
> > > 	PR c++/108474
> > > 	* cgraphbuild.cc: Include gimplify.h.
> > > 	(record_reference): Replace VAR_DECLs with DECL_HAS_VALUE_EXPR_P with
> > > 	their corresponding DECL_VALUE_EXPR expressions after unsharing.
> > > 
> > > 	* cp-gimplify.cc (cp_fold_r): Revert 2023-01-19 changes.
> > > 
> > > 	* g++.dg/cpp1z/decomp57.C: New test.
> > > 	* g++.dg/cpp1z/decomp58.C: New test.
> > > 
> > > --- gcc/cgraphbuild.cc.jj	2023-01-02 09:32:34.889104620 +0100
> > > +++ gcc/cgraphbuild.cc	2023-01-23 12:10:35.042067571 +0100
> > > @@ -31,6 +31,7 @@ along with GCC; see the file COPYING3.
> > >  #include "gimple-walk.h"
> > >  #include "ipa-utils.h"
> > >  #include "except.h"
> > > +#include "gimplify.h"
> > >  
> > >  /* Context of record_reference.  */
> > >  struct record_reference_ctx
> > > @@ -79,6 +80,17 @@ record_reference (tree *tp, int *walk_su
> > >  
> > >        if (VAR_P (decl))
> > >  	{
> > > +	  /* Replace vars with their DECL_VALUE_EXPR if any.
> > > +	     This is normally done during gimplification, but
> > > +	     static var initializers are never gimplified.  */
> > > +	  if (DECL_HAS_VALUE_EXPR_P (decl))
> > > +	    {
> > > +	      tree *p;
> > > +	      for (p = tp; *p != decl; p = &TREE_OPERAND (*p, 0))
> > > +		;
> > > +	      *p = unshare_expr (DECL_VALUE_EXPR (decl));
> > > +	      return record_reference (tp, walk_subtrees, data);
> > > +	    }
> > >  	  varpool_node *vnode = varpool_node::get_create (decl);
> > >  	  ctx->varpool_node->create_reference (vnode, IPA_REF_ADDR);
> > >  	}
> > > --- gcc/cp/cp-gimplify.cc.jj	2023-01-23 11:47:49.889875804 +0100
> > > +++ gcc/cp/cp-gimplify.cc	2023-01-23 11:49:01.227841759 +0100
> > > @@ -1010,16 +1010,6 @@ cp_fold_r (tree *stmt_p, int *walk_subtr
> > >  	}
> > >        break;
> > >  
> > > -    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)
> > > -	{
> > > -	  *stmt_p = stmt = unshare_expr (DECL_VALUE_EXPR (stmt));
> > > -	  break;
> > > -	}
> > > -      break;
> > > -
> > >      default:
> > >        break;
> > >      }
> > > --- gcc/testsuite/g++.dg/cpp1z/decomp57.C.jj	2023-01-23 11:48:36.367202107 +0100
> > > +++ gcc/testsuite/g++.dg/cpp1z/decomp57.C	2023-01-23 11:48:36.367202107 +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-23 11:48:36.367202107 +0100
> > > +++ gcc/testsuite/g++.dg/cpp1z/decomp58.C	2023-01-23 11:48:36.367202107 +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
> 
>
  

Patch

--- gcc/cgraphbuild.cc.jj	2023-01-02 09:32:34.889104620 +0100
+++ gcc/cgraphbuild.cc	2023-01-23 12:10:35.042067571 +0100
@@ -31,6 +31,7 @@  along with GCC; see the file COPYING3.
 #include "gimple-walk.h"
 #include "ipa-utils.h"
 #include "except.h"
+#include "gimplify.h"
 
 /* Context of record_reference.  */
 struct record_reference_ctx
@@ -79,6 +80,17 @@  record_reference (tree *tp, int *walk_su
 
       if (VAR_P (decl))
 	{
+	  /* Replace vars with their DECL_VALUE_EXPR if any.
+	     This is normally done during gimplification, but
+	     static var initializers are never gimplified.  */
+	  if (DECL_HAS_VALUE_EXPR_P (decl))
+	    {
+	      tree *p;
+	      for (p = tp; *p != decl; p = &TREE_OPERAND (*p, 0))
+		;
+	      *p = unshare_expr (DECL_VALUE_EXPR (decl));
+	      return record_reference (tp, walk_subtrees, data);
+	    }
 	  varpool_node *vnode = varpool_node::get_create (decl);
 	  ctx->varpool_node->create_reference (vnode, IPA_REF_ADDR);
 	}
--- gcc/cp/cp-gimplify.cc.jj	2023-01-23 11:47:49.889875804 +0100
+++ gcc/cp/cp-gimplify.cc	2023-01-23 11:49:01.227841759 +0100
@@ -1010,16 +1010,6 @@  cp_fold_r (tree *stmt_p, int *walk_subtr
 	}
       break;
 
-    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)
-	{
-	  *stmt_p = stmt = unshare_expr (DECL_VALUE_EXPR (stmt));
-	  break;
-	}
-      break;
-
     default:
       break;
     }
--- gcc/testsuite/g++.dg/cpp1z/decomp57.C.jj	2023-01-23 11:48:36.367202107 +0100
+++ gcc/testsuite/g++.dg/cpp1z/decomp57.C	2023-01-23 11:48:36.367202107 +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-23 11:48:36.367202107 +0100
+++ gcc/testsuite/g++.dg/cpp1z/decomp58.C	2023-01-23 11:48:36.367202107 +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 ()
+{
+}