public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH] c++: Fix up mangling of function/block scope static structured bindings [PR111069]
@ 2023-08-22  8:12 Jakub Jelinek
  2023-08-23 20:23 ` Jason Merrill
  0 siblings, 1 reply; 7+ messages in thread
From: Jakub Jelinek @ 2023-08-22  8:12 UTC (permalink / raw)
  To: Jason Merrill; +Cc: gcc-patches

Hi!

As can be seen on the testcase, we weren't correctly mangling
static/thread_local structured bindings (C++20 feature) at function/block
scope.  The following patch fixes that by using what write_local_name
does for those cases (note, structured binding mandling doesn't use the
standard path because it needs to pass a list of all the identifiers in
the structured binding to the mangling).  In addition to that it fixes
mangling of various helpers which use write_guarded_name (_ZGV*, _ZTH*,
_ZTW*) and kills find_decomp_unqualified_name which for the local names
would be too hard to implement and uses write_guarded_name for structured
binding related _ZGR* names as well.
All the mangled names on the testcase match now clang++ and my expectations.
Because the old mangled names were plain wrong (they mangled the same as
structured binding at global scope and resulted in assembly errors if there
was more than one static structured binding with the same identifiers in
the same (or another) function, I think we don't need to play with another
mangling ABI level which turns on/off the old broken way, unsure whether
we should backport the patch to 13 branch though.

BTW, I think we should also handle ABI tags in mangle_decomp which we
currently don't do, but guess that should be subject to another mangling ABI
version.
On
struct __attribute__((abi_tag ("foobar"))) S { int i; };
extern S a[2];
struct __attribute__((abi_tag ("qux"))) T { int i; S j; int k; };
extern T b[2];
namespace N { auto [i, j] = a; auto [k, l] = b; S c; T d; }
inline void foo () { static auto [m, n] = a; static auto [o, p] = b;
{ static auto [m, n] = a; ++n.i; } ++m.i; ++o.i; }
void (*p) () = &foo;
clang++ uses
_ZN1NDC1i1jEB6foobarE
_ZN1NDC1k1lEB3quxE
_ZZ3foovEDC1m1nEB6foobar
_ZGVZ3foovEDC1m1nEB6foobar
_ZZ3foovEDC1o1pEB3qux
_ZGVZ3foovEDC1o1pEB3qux
_ZZ3foovEDC1m1nEB6foobar_0
_ZGVZ3foovEDC1m1nEB6foobar_0
mangling while g++ (with the patch):
_ZN1NDC1i1jEE
_ZN1NDC1k1lEE
_ZZ3foovEDC1m1nE
_ZGVZ3foovEDC1m1nE
_ZZ3foovEDC1o1pE
_ZGVZ3foovEDC1o1pE
_ZZ3foovEDC1m1nE_0
_ZGVZ3foovEDC1m1nE_0

Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

2023-08-22  Jakub Jelinek  <jakub@redhat.com>

	PR c++/111069
	* cp-tree.h (determine_local_discriminator): Add NAME argument with
	NULL_TREE default.
	* decl.cc (determine_local_discriminator): Add NAME argument, use it
	if non-NULL, otherwise compute it the old way.
	(cp_maybe_mangle_decomp): Call determine_local_discriminator.
	* mangle.cc (find_decomp_unqualified_name): Remove.
	(write_unqualified_name): Don't call find_decomp_unqualified_name.
	(mangle_decomp): Handle mangling of static function/block scope
	structured bindings.  Don't call decl_mangling_context twice.
	(write_guarded_var_name): Handle structured bindings.
	(mangle_ref_init_variable): Use write_guarded_var_name for structured
	bindings.

	* g++.dg/cpp2a/decomp8.C: New test.

--- gcc/cp/cp-tree.h.jj	2023-08-18 20:11:11.013692860 +0200
+++ gcc/cp/cp-tree.h	2023-08-19 10:45:00.320543681 +0200
@@ -6858,7 +6858,7 @@ extern void pop_switch				(void);
 extern void note_break_stmt			(void);
 extern bool note_iteration_stmt_body_start	(void);
 extern void note_iteration_stmt_body_end	(bool);
-extern void determine_local_discriminator	(tree);
+extern void determine_local_discriminator	(tree, tree = NULL_TREE);
 extern bool fns_correspond			(tree, tree);
 extern int decls_match				(tree, tree, bool = true);
 extern bool maybe_version_functions		(tree, tree, bool);
--- gcc/cp/decl.cc.jj	2023-08-18 09:49:38.899871662 +0200
+++ gcc/cp/decl.cc	2023-08-19 10:52:11.748888997 +0200
@@ -913,15 +913,16 @@ static GTY((deletable)) vec<tree, va_gc>
    generally very few of these in any particular function.  */
 
 void
-determine_local_discriminator (tree decl)
+determine_local_discriminator (tree decl, tree name)
 {
   auto_cond_timevar tv (TV_NAME_LOOKUP);
   retrofit_lang_decl (decl);
   tree ctx = DECL_CONTEXT (decl);
-  tree name = (TREE_CODE (decl) == TYPE_DECL
-	       && TYPE_UNNAMED_P (TREE_TYPE (decl))
-	       ? NULL_TREE : DECL_NAME (decl));
   size_t nelts = vec_safe_length (local_entities);
+  if (name == NULL_TREE)
+    name = (TREE_CODE (decl) == TYPE_DECL
+	    && TYPE_UNNAMED_P (TREE_TYPE (decl))
+	    ? NULL_TREE : DECL_NAME (decl));
   for (size_t i = 0; i < nelts; i += 2)
     {
       tree *pair = &(*local_entities)[i];
@@ -9049,6 +9050,25 @@ cp_maybe_mangle_decomp (tree decl, tree
       tree d = first;
       for (unsigned int i = 0; i < count; i++, d = DECL_CHAIN (d))
 	v[count - i - 1] = d;
+      if (DECL_FUNCTION_SCOPE_P (decl))
+	{
+	  size_t sz = 3;
+	  for (unsigned int i = 0; i < count; ++i)
+	    sz += IDENTIFIER_LENGTH (DECL_NAME (v[i])) + 1;
+	  char *name = XALLOCAVEC (char, sz);
+	  name[0] = 'D';
+	  name[1] = 'C';
+	  char *p = name + 2;
+	  for (unsigned int i = 0; i < count; ++i)
+	    {
+	      size_t len = IDENTIFIER_LENGTH (DECL_NAME (v[i]));
+	      *p++ = ' ';
+	      memcpy (p, IDENTIFIER_POINTER (DECL_NAME (v[i])), len);
+	      p += len;
+	    }
+	  *p = '\0';
+	  determine_local_discriminator (decl, get_identifier (name));
+	}
       SET_DECL_ASSEMBLER_NAME (decl, mangle_decomp (decl, v));
       maybe_apply_pragma_weak (decl);
     }
--- gcc/cp/mangle.cc.jj	2023-08-08 15:55:06.218168490 +0200
+++ gcc/cp/mangle.cc	2023-08-21 10:39:35.209155346 +0200
@@ -1344,51 +1344,6 @@ write_template_prefix (const tree node)
   add_substitution (substitution);
 }
 
-/* As the list of identifiers for the structured binding declaration
-   DECL is likely gone, try to recover the DC <source-name>+ E portion
-   from its mangled name.  Return pointer to the DC and set len to
-   the length up to and including the terminating E.  On failure
-   return NULL.  */
-
-static const char *
-find_decomp_unqualified_name (tree decl, size_t *len)
-{
-  const char *p = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl));
-  const char *end = p + IDENTIFIER_LENGTH (DECL_ASSEMBLER_NAME (decl));
-  bool nested = false;
-  if (!startswith (p, "_Z"))
-    return NULL;
-  p += 2;
-  if (startswith (p, "St"))
-    p += 2;
-  else if (*p == 'N')
-    {
-      nested = true;
-      ++p;
-      while (ISDIGIT (p[0]))
-	{
-	  char *e;
-	  long num = strtol (p, &e, 10);
-	  if (num >= 1 && num < end - e)
-	    p = e + num;
-	  else
-	    break;
-	}
-    }
-  if (!startswith (p, "DC"))
-    return NULL;
-  if (nested)
-    {
-      if (end[-1] != 'E')
-	return NULL;
-      --end;
-    }
-  if (end[-1] != 'E')
-    return NULL;
-  *len = end - p;
-  return p;
-}
-
 /* "For the purposes of mangling, the name of an anonymous union is considered
    to be the name of the first named data member found by a pre-order,
    depth-first, declaration-order walk of the data members of the anonymous
@@ -1461,17 +1416,7 @@ write_unqualified_name (tree decl)
     {
       found = true;
       gcc_assert (DECL_ASSEMBLER_NAME_SET_P (decl));
-      const char *decomp_str = NULL;
-      size_t decomp_len = 0;
-      if (VAR_P (decl)
-	  && DECL_DECOMPOSITION_P (decl)
-	  && DECL_NAME (decl) == NULL_TREE
-	  && DECL_NAMESPACE_SCOPE_P (decl))
-	decomp_str = find_decomp_unqualified_name (decl, &decomp_len);
-      if (decomp_str)
-	write_chars (decomp_str, decomp_len);
-      else
-	write_source_name (DECL_ASSEMBLER_NAME (decl));
+      write_source_name (DECL_ASSEMBLER_NAME (decl));
     }
   else if (DECL_DECLARES_FUNCTION_P (decl))
     {
@@ -4370,13 +4315,21 @@ mangle_decomp (const tree decl, vec<tree
   gcc_assert (context != NULL_TREE);
 
   bool nested = false;
+  bool local = false;
   if (DECL_NAMESPACE_STD_P (context))
     write_string ("St");
+  else if (TREE_CODE (context) == FUNCTION_DECL)
+    {
+      local = true;
+      write_char ('Z');
+      write_encoding (context);
+      write_char ('E');
+    }
   else if (context != global_namespace)
     {
       nested = true;
       write_char ('N');
-      write_prefix (decl_mangling_context (decl));
+      write_prefix (context);
     }
 
   write_string ("DC");
@@ -4388,6 +4341,8 @@ mangle_decomp (const tree decl, vec<tree
 
   if (nested)
     write_char ('E');
+  else if (local && DECL_DISCRIMINATOR_P (decl))
+    write_discriminator (discriminator_for_local_entity (decl));
 
   tree id = finish_mangling_get_identifier ();
   if (DEBUG_MANGLE)
@@ -4564,6 +4519,13 @@ write_guarded_var_name (const tree varia
     /* The name of a guard variable for a reference temporary should refer
        to the reference, not the temporary.  */
     write_string (IDENTIFIER_POINTER (DECL_NAME (variable)) + 4);
+  else if (DECL_DECOMPOSITION_P (variable)
+	   && DECL_NAME (variable) == NULL_TREE
+	   && startswith (IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (variable)),
+			  "_Z"))
+    /* The name of a guard variable for a structured binding needs special
+       casing.  */
+    write_string (IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (variable)) + 2);
   else
     write_name (variable, /*ignore_local_scope=*/0);
 }
@@ -4630,7 +4592,10 @@ mangle_ref_init_variable (const tree var
   start_mangling (variable);
   write_string ("_ZGR");
   check_abi_tags (variable);
-  write_name (variable, /*ignore_local_scope=*/0);
+  if (DECL_DECOMPOSITION_P (variable))
+    write_guarded_var_name (variable);
+  else
+    write_name (variable, /*ignore_local_scope=*/0);
   /* Avoid name clashes with aggregate initialization of multiple
      references at once.  */
   write_compact_number (current_ref_temp_count++);
--- gcc/testsuite/g++.dg/cpp2a/decomp8.C.jj	2023-08-21 10:56:02.653308449 +0200
+++ gcc/testsuite/g++.dg/cpp2a/decomp8.C	2023-08-21 11:05:13.349163678 +0200
@@ -0,0 +1,70 @@
+// PR c++/111069
+// { dg-do compile { target c++11 } }
+// { dg-options "" }
+
+extern int a[2];
+struct Y { int b, c, d; };
+
+inline void
+freddy ()
+{
+  static auto [i, j] = a;		// { dg-warning "structured bindings only available with" "" { target c++14_down } }
+					// { dg-warning "structured binding declaration can be 'static' only in" "" { target c++17_down } .-1 }
+  static auto [k, l] = a;		// { dg-warning "structured bindings only available with" "" { target c++14_down } }
+					// { dg-warning "structured binding declaration can be 'static' only in" "" { target c++17_down } .-1 }
+  {
+    static auto [i, j] = a;		// { dg-warning "structured bindings only available with" "" { target c++14_down } }
+					// { dg-warning "structured binding declaration can be 'static' only in" "" { target c++17_down } .-1 }
+    static auto [k, l] = a;		// { dg-warning "structured bindings only available with" "" { target c++14_down } }
+					// { dg-warning "structured binding declaration can be 'static' only in" "" { target c++17_down } .-1 }
+  }
+  {
+    static auto [i, j] = a;		// { dg-warning "structured bindings only available with" "" { target c++14_down } }
+					// { dg-warning "structured binding declaration can be 'static' only in" "" { target c++17_down } .-1 }
+    static auto [k, l] = a;		// { dg-warning "structured bindings only available with" "" { target c++14_down } }
+					// { dg-warning "structured binding declaration can be 'static' only in" "" { target c++17_down } .-1 }
+  }
+}
+
+namespace N
+{
+  namespace M
+  {
+    template <int N>
+    inline void corge ()
+    {
+      static auto [i, j] = a;		// { dg-warning "structured bindings only available with" "" { target c++14_down } }
+					// { dg-warning "structured binding declaration can be 'static' only in" "" { target c++17_down } .-1 }
+      static auto && [u, v, w] = Y{};	// { dg-warning "structured bindings only available with" "" { target c++14_down } }
+					// { dg-warning "structured binding declaration can be 'static' only in" "" { target c++17_down } .-1 }
+      ++u;
+      {
+	static auto && [u, v, w] = Y{};	// { dg-warning "structured bindings only available with" "" { target c++14_down } }
+					// { dg-warning "structured binding declaration can be 'static' only in" "" { target c++17_down } .-1 }
+	++v;
+      }
+    }
+  }
+}
+
+void (*p) () = &freddy;
+void (*q) () = N::M::corge<3>;
+
+// { dg-final { scan-assembler "_ZZ6freddyvEDC1i1jE" } }
+// { dg-final { scan-assembler "_ZZ6freddyvEDC1i1jE_0" } }
+// { dg-final { scan-assembler "_ZZ6freddyvEDC1i1jE_1" } }
+// { dg-final { scan-assembler "_ZZ6freddyvEDC1k1lE" } }
+// { dg-final { scan-assembler "_ZZ6freddyvEDC1k1lE_0" } }
+// { dg-final { scan-assembler "_ZZ6freddyvEDC1k1lE_1" } }
+// { dg-final { scan-assembler "_ZZN1N1M5corgeILi3EEEvvEDC1i1jE" } }
+// { dg-final { scan-assembler "_ZZN1N1M5corgeILi3EEEvvEDC1u1v1wE" } }
+// { dg-final { scan-assembler "_ZZN1N1M5corgeILi3EEEvvEDC1u1v1wE_0" } }
+// { dg-final { scan-assembler "_ZGVZ6freddyvEDC1i1jE" } }
+// { dg-final { scan-assembler "_ZGVZ6freddyvEDC1i1jE_0" } }
+// { dg-final { scan-assembler "_ZGVZ6freddyvEDC1i1jE_1" } }
+// { dg-final { scan-assembler "_ZGVZ6freddyvEDC1k1lE" } }
+// { dg-final { scan-assembler "_ZGVZ6freddyvEDC1k1lE_0" } }
+// { dg-final { scan-assembler "_ZGVZ6freddyvEDC1k1lE_1" } }
+// { dg-final { scan-assembler "_ZGVZN1N1M5corgeILi3EEEvvEDC1i1jE" } }
+// { dg-final { scan-assembler "_ZGRZN1N1M5corgeILi3EEEvvEDC1u1v1wE_" } }
+// { dg-final { scan-assembler "_ZGRZN1N1M5corgeILi3EEEvvEDC1u1v1wE_0_" } }

	Jakub


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

* Re: [PATCH] c++: Fix up mangling of function/block scope static structured bindings [PR111069]
  2023-08-22  8:12 [PATCH] c++: Fix up mangling of function/block scope static structured bindings [PR111069] Jakub Jelinek
@ 2023-08-23 20:23 ` Jason Merrill
  2023-08-24 16:39   ` Jakub Jelinek
  0 siblings, 1 reply; 7+ messages in thread
From: Jason Merrill @ 2023-08-23 20:23 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: gcc-patches

On 8/22/23 04:12, Jakub Jelinek wrote:
> As can be seen on the testcase, we weren't correctly mangling
> static/thread_local structured bindings (C++20 feature) at function/block
> scope.  The following patch fixes that by using what write_local_name
> does for those cases (note, structured binding mandling doesn't use the
> standard path because it needs to pass a list of all the identifiers in
> the structured binding to the mangling).  In addition to that it fixes
> mangling of various helpers which use write_guarded_name (_ZGV*, _ZTH*,
> _ZTW*) and kills find_decomp_unqualified_name which for the local names
> would be too hard to implement and uses write_guarded_name for structured
> binding related _ZGR* names as well.
> All the mangled names on the testcase match now clang++ and my expectations.
> Because the old mangled names were plain wrong (they mangled the same as
> structured binding at global scope and resulted in assembly errors if there
> was more than one static structured binding with the same identifiers in
> the same (or another) function, I think we don't need to play with another
> mangling ABI level which turns on/off the old broken way, unsure whether
> we should backport the patch to 13 branch though.

Probably not.

> BTW, I think we should also handle ABI tags in mangle_decomp which we
> currently don't do, but guess that should be subject to another mangling ABI
> version.

I'd be surprised if this would affect any real code, but I suppose so. 
In any case I'd like to fix this at the same time as the local statics, 
to avoid changing their mangled name twice.

> @@ -9049,6 +9050,25 @@ cp_maybe_mangle_decomp (tree decl, tree
>         tree d = first;
>         for (unsigned int i = 0; i < count; i++, d = DECL_CHAIN (d))
>   	v[count - i - 1] = d;
> +      if (DECL_FUNCTION_SCOPE_P (decl))
> +	{
> +	  size_t sz = 3;
> +	  for (unsigned int i = 0; i < count; ++i)
> +	    sz += IDENTIFIER_LENGTH (DECL_NAME (v[i])) + 1;
> +	  char *name = XALLOCAVEC (char, sz);
> +	  name[0] = 'D';
> +	  name[1] = 'C';
> +	  char *p = name + 2;
> +	  for (unsigned int i = 0; i < count; ++i)
> +	    {
> +	      size_t len = IDENTIFIER_LENGTH (DECL_NAME (v[i]));
> +	      *p++ = ' ';
> +	      memcpy (p, IDENTIFIER_POINTER (DECL_NAME (v[i])), len);
> +	      p += len;
> +	    }
> +	  *p = '\0';
> +	  determine_local_discriminator (decl, get_identifier (name));
> +	}

Maybe do this in mangle_decomp, based on the actual mangling in process 
instead of this pseudo-mangling?

> @@ -4564,6 +4519,13 @@ write_guarded_var_name (const tree varia
>       /* The name of a guard variable for a reference temporary should refer
>          to the reference, not the temporary.  */
>       write_string (IDENTIFIER_POINTER (DECL_NAME (variable)) + 4);
> +  else if (DECL_DECOMPOSITION_P (variable)
> +	   && DECL_NAME (variable) == NULL_TREE
> +	   && startswith (IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (variable)),
> +			  "_Z"))

Maybe add a startswith overload that takes an identifier?

> @@ -4630,7 +4592,10 @@ mangle_ref_init_variable (const tree var
>     start_mangling (variable);
>     write_string ("_ZGR");
>     check_abi_tags (variable);
> -  write_name (variable, /*ignore_local_scope=*/0);
> +  if (DECL_DECOMPOSITION_P (variable))
> +    write_guarded_var_name (variable);
> +  else
> +    write_name (variable, /*ignore_local_scope=*/0);

Why not use write_guarded_name unconditionally?

Jason


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

* Re: [PATCH] c++: Fix up mangling of function/block scope static structured bindings [PR111069]
  2023-08-23 20:23 ` Jason Merrill
@ 2023-08-24 16:39   ` Jakub Jelinek
  2023-08-28 13:58     ` [PATCH] c++, v2: Fix up mangling of function/block scope static structured bindings and emit abi tags [PR111069] Jakub Jelinek
  0 siblings, 1 reply; 7+ messages in thread
From: Jakub Jelinek @ 2023-08-24 16:39 UTC (permalink / raw)
  To: Jason Merrill; +Cc: gcc-patches

On Wed, Aug 23, 2023 at 04:23:00PM -0400, Jason Merrill wrote:
> I'd be surprised if this would affect any real code, but I suppose so. In
> any case I'd like to fix this at the same time as the local statics, to
> avoid changing their mangled name twice.

Ok.
Running now into a problem with abi tags, because cp_maybe_mangle_decomp
is called before the type of the structured binding is finalized (sequence
is cp_maybe_mangle_decomp; cp_finish_decl; cp_finish_decomp), I vaguely
remember the reason was to have the name already mangled by cp_finish_decl
time, so that it can add it into varpool etc..
Will see if I can e.g. pass the initializer expression to
cp_maybe_mangle_decomp and figure out the tags from that.

> > @@ -9049,6 +9050,25 @@ cp_maybe_mangle_decomp (tree decl, tree
> >         tree d = first;
> >         for (unsigned int i = 0; i < count; i++, d = DECL_CHAIN (d))
> >   	v[count - i - 1] = d;
> > +      if (DECL_FUNCTION_SCOPE_P (decl))
> > +	{
> > +	  size_t sz = 3;
> > +	  for (unsigned int i = 0; i < count; ++i)
> > +	    sz += IDENTIFIER_LENGTH (DECL_NAME (v[i])) + 1;
> > +	  char *name = XALLOCAVEC (char, sz);
> > +	  name[0] = 'D';
> > +	  name[1] = 'C';
> > +	  char *p = name + 2;
> > +	  for (unsigned int i = 0; i < count; ++i)
> > +	    {
> > +	      size_t len = IDENTIFIER_LENGTH (DECL_NAME (v[i]));
> > +	      *p++ = ' ';
> > +	      memcpy (p, IDENTIFIER_POINTER (DECL_NAME (v[i])), len);
> > +	      p += len;
> > +	    }
> > +	  *p = '\0';
> > +	  determine_local_discriminator (decl, get_identifier (name));
> > +	}
> 
> Maybe do this in mangle_decomp, based on the actual mangling in process
> instead of this pseudo-mangling?

Not sure that is possible, for 2 reasons:
1) determine_local_discriminator otherwise works on DECL_NAME, not mangled
   names, so if one uses (albeit implementation reserved)
   _ZZN1N3fooI1TB3bazEEivEDC1h1iEB6foobar and similar identifiers, they
   could clash with the counting of the structured bindings
2) seems the local discriminator counting shouldn't take into account
   details like abi tags, e.g. if I have:
struct [[gnu::abi_tag ("foobar")]] S { int a; };
namespace N {
  template <typename T>
  inline int
  foo ()
  {
    static int h = 42; int r = ++h;
    { static S h = { 42 }; r += ++h.a; }
    { static int h = 42; r += ++h; }
    { static S h = { 42 }; r += ++h.a; }
    return r;
  }
}
int (*p) () = N::foo<int>;
  then both GCC and Clang mangle these as
  _ZZN1N3fooIiEEivE1h
  _ZZN1N3fooIiEEivE1hB6foobar_0
  _ZZN1N3fooIiEEivE1h_1
  _ZZN1N3fooIiEEivE1hB6foobar_2
  so whether abi tags appear in the mangled name or not shouldn't result
  in separate buckets for counting.

> 
> > @@ -4564,6 +4519,13 @@ write_guarded_var_name (const tree varia
> >       /* The name of a guard variable for a reference temporary should refer
> >          to the reference, not the temporary.  */
> >       write_string (IDENTIFIER_POINTER (DECL_NAME (variable)) + 4);
> > +  else if (DECL_DECOMPOSITION_P (variable)
> > +	   && DECL_NAME (variable) == NULL_TREE
> > +	   && startswith (IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (variable)),
> > +			  "_Z"))
> 
> Maybe add a startswith overload that takes an identifier?

Ok.

> > @@ -4630,7 +4592,10 @@ mangle_ref_init_variable (const tree var
> >     start_mangling (variable);
> >     write_string ("_ZGR");
> >     check_abi_tags (variable);
> > -  write_name (variable, /*ignore_local_scope=*/0);
> > +  if (DECL_DECOMPOSITION_P (variable))
> > +    write_guarded_var_name (variable);
> > +  else
> > +    write_name (variable, /*ignore_local_scope=*/0);
> 
> Why not use write_guarded_name unconditionally?

Ok.

	Jakub


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

* [PATCH] c++, v2: Fix up mangling of function/block scope static structured bindings and emit abi tags [PR111069]
  2023-08-24 16:39   ` Jakub Jelinek
@ 2023-08-28 13:58     ` Jakub Jelinek
  2023-08-31 17:11       ` Jason Merrill
  0 siblings, 1 reply; 7+ messages in thread
From: Jakub Jelinek @ 2023-08-28 13:58 UTC (permalink / raw)
  To: Jason Merrill; +Cc: gcc-patches

Hi!

On Thu, Aug 24, 2023 at 06:39:10PM +0200, Jakub Jelinek via Gcc-patches wrote:
> > Maybe do this in mangle_decomp, based on the actual mangling in process
> > instead of this pseudo-mangling?
> 
> Not sure that is possible, for 2 reasons:
> 1) determine_local_discriminator otherwise works on DECL_NAME, not mangled
>    names, so if one uses (albeit implementation reserved)
>    _ZZN1N3fooI1TB3bazEEivEDC1h1iEB6foobar and similar identifiers, they
>    could clash with the counting of the structured bindings
> 2) seems the local discriminator counting shouldn't take into account
>    details like abi tags, e.g. if I have:

The following updated patch handles everything except it leaves for the
above 2 reasons the determination of local discriminator where it was.
I had to add a new (defaulted) argument to cp_finish_decl and do
cp_maybe_mangle_decomp from there, so that it is after e.g. auto type
deduction and maybe_commonize_var (which had to be changed as well) and
spots in cp_finish_decl where we need or might need mangled names already.

Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

There is one difference between g++ with this patch and clang++,
g++ uses
_ZZ3barI1TB3quxEivEDC1o1pEB3qux
while clang++ uses
_ZZ3barI1TB3quxEivEDC1o1pE
but from what I can see, such a difference is there also when just using
normal local decls:
struct [[gnu::abi_tag ("foobar")]] S { int i; };
struct [[gnu::abi_tag ("qux")]] T { int i; S j; int k; };

inline int
foo ()
{
  static S c;
  static T d;
  return ++c.i + ++d.i;
}

template <typename T>
inline int
bar ()
{
  static S c;
  static T d;
  return ++c.i + ++d.i;
}

int (*p) () = &foo;
int (*q) () = &bar<T>;
where both compilers mangle c in foo as:
_ZZ3foovE1cB6foobar
and d in there as
_ZZ3foovE1dB3qux
and similarly both compilers mangle c in bar as
_ZZ3barI1TB3quxEivE1cB6foobar
but g++ mangles d in bar as
_ZZ3barI1TB3quxEivE1dB3qux
while clang++ mangles it as just
_ZZ3barI1TB3quxEivE1d
No idea what is right or wrong according to Itanium mangling.

2023-08-28  Jakub Jelinek  <jakub@redhat.com>

	PR c++/111069
gcc/
	* common.opt (fabi-version=): Document version 19.
	* doc/invoke.texi (-fabi-version=): Likewise.
gcc/c-family/
	* c-opts.cc (c_common_post_options): Change latest_abi_version to 19.
gcc/cp/
	* cp-tree.h (determine_local_discriminator): Add NAME argument with
	NULL_TREE default.
	(struct cp_decomp): New type.
	(cp_finish_decl): Add DECOMP argument defaulted to nullptr.
	(cp_maybe_mangle_decomp): Remove declaration.
	* decl.cc (determine_local_discriminator): Add NAME argument, use it
	if non-NULL, otherwise compute it the old way.
	(maybe_commonize_var): Don't return early for structured bindings.
	(cp_finish_decl): Add DECOMP argument, if non-NULL, call
	cp_maybe_mangle_decomp.
	(cp_maybe_mangle_decomp): Make it static with a forward declaration.
	Call determine_local_discriminator.
	* mangle.cc (find_decomp_unqualified_name): Remove.
	(write_unqualified_name): Don't call find_decomp_unqualified_name.
	(mangle_decomp): Handle mangling of static function/block scope
	structured bindings.  Don't call decl_mangling_context twice.  Call
	check_abi_tags, call write_abi_tags for abi version >= 19 and emit
	-Wabi warnings if needed.
	(write_guarded_var_name): Handle structured bindings.
	(mangle_ref_init_variable): Use write_guarded_var_name.
	* parser.cc (cp_convert_range_for, cp_parser_decomposition_declaration,
	cp_finish_omp_range_for): Don't call cp_maybe_mangle_decomp, adjust
	cp_finish_decl callers.
	* pt.cc (tsubst_expr): Likewise.
gcc/testsuite/
	* g++.dg/cpp2a/decomp8.C: New test.
	* g++.dg/cpp2a/decomp9.C: New test.
	* g++.dg/abi/macro0.C: Expect __GXX_ABI_VERSION 1019 rather than
	1018.

--- gcc/common.opt.jj	2023-08-28 10:32:41.519579280 +0200
+++ gcc/common.opt	2023-08-28 10:35:30.337342832 +0200
@@ -1010,6 +1010,9 @@ Driver Undocumented
 ; 18: Corrects errors in mangling of lambdas with additional context.
 ;     Default in G++ 13.
 ;
+; 19: Emits ABI tags if needed in structured binding mangled names.
+;     Default in G++ 14.
+;
 ; Additional positive integers will be assigned as new versions of
 ; the ABI become the default version of the ABI.
 fabi-version=
--- gcc/doc/invoke.texi.jj	2023-08-28 10:32:42.322568643 +0200
+++ gcc/doc/invoke.texi	2023-08-28 10:35:30.342342766 +0200
@@ -3016,6 +3016,9 @@ in C++14 and up.
 Version 18, which first appeard in G++ 13, fixes manglings of lambdas
 that have additional context.
 
+Version 19, which first appeard in G++ 14, fixes manglings of structured
+bindings to include ABI tags.
+
 See also @option{-Wabi}.
 
 @opindex fabi-compat-version
--- gcc/c-family/c-opts.cc.jj	2023-08-28 10:32:41.462580035 +0200
+++ gcc/c-family/c-opts.cc	2023-08-28 10:35:30.338342819 +0200
@@ -974,7 +974,7 @@ c_common_post_options (const char **pfil
 
   /* Change flag_abi_version to be the actual current ABI level, for the
      benefit of c_cpp_builtins, and to make comparison simpler.  */
-  const int latest_abi_version = 18;
+  const int latest_abi_version = 19;
   /* Generate compatibility aliases for ABI v13 (8.2) by default.  */
   const int abi_compat_default = 13;
 
--- gcc/cp/cp-tree.h.jj	2023-08-28 10:33:10.972189104 +0200
+++ gcc/cp/cp-tree.h	2023-08-28 10:41:19.782716871 +0200
@@ -6858,7 +6858,7 @@ extern void pop_switch				(void);
 extern void note_break_stmt			(void);
 extern bool note_iteration_stmt_body_start	(void);
 extern void note_iteration_stmt_body_end	(bool);
-extern void determine_local_discriminator	(tree);
+extern void determine_local_discriminator	(tree, tree = NULL_TREE);
 extern bool member_like_constrained_friend_p	(tree);
 extern bool fns_correspond			(tree, tree);
 extern int decls_match				(tree, tree, bool = true);
@@ -6891,9 +6891,9 @@ extern tree start_decl				(const cp_decl
 extern void start_decl_1			(tree, bool);
 extern bool check_array_initializer		(tree, tree, tree);
 extern void omp_declare_variant_finalize	(tree, tree);
-extern void cp_finish_decl			(tree, tree, bool, tree, int);
+struct cp_decomp { tree decl; unsigned int count; };
+extern void cp_finish_decl			(tree, tree, bool, tree, int, cp_decomp * = nullptr);
 extern tree lookup_decomp_type			(tree);
-extern void cp_maybe_mangle_decomp		(tree, tree, unsigned int);
 extern void cp_finish_decomp			(tree, tree, unsigned int);
 extern int cp_complete_array_type		(tree *, tree, bool);
 extern int cp_complete_array_type_or_error	(tree *, tree, bool, tsubst_flags_t);
--- gcc/cp/decl.cc.jj	2023-08-28 10:32:42.170570656 +0200
+++ gcc/cp/decl.cc	2023-08-28 12:10:38.973074234 +0200
@@ -911,15 +911,16 @@ static GTY((deletable)) vec<tree, va_gc>
    generally very few of these in any particular function.  */
 
 void
-determine_local_discriminator (tree decl)
+determine_local_discriminator (tree decl, tree name)
 {
   auto_cond_timevar tv (TV_NAME_LOOKUP);
   retrofit_lang_decl (decl);
   tree ctx = DECL_CONTEXT (decl);
-  tree name = (TREE_CODE (decl) == TYPE_DECL
-	       && TYPE_UNNAMED_P (TREE_TYPE (decl))
-	       ? NULL_TREE : DECL_NAME (decl));
   size_t nelts = vec_safe_length (local_entities);
+  if (name == NULL_TREE)
+    name = (TREE_CODE (decl) == TYPE_DECL
+	    && TYPE_UNNAMED_P (TREE_TYPE (decl))
+	    ? NULL_TREE : DECL_NAME (decl));
   for (size_t i = 0; i < nelts; i += 2)
     {
       tree *pair = &(*local_entities)[i];
@@ -6417,8 +6418,9 @@ layout_var_decl (tree decl)
 void
 maybe_commonize_var (tree decl)
 {
-  /* Don't mess with __FUNCTION__ and similar.  */
-  if (DECL_ARTIFICIAL (decl))
+  /* Don't mess with __FUNCTION__ and similar.  But do handle structured
+     bindings.  */
+  if (DECL_ARTIFICIAL (decl) && !DECL_DECOMPOSITION_P (decl))
     return;
 
   /* Static data in a function with comdat linkage also has comdat
@@ -8212,6 +8214,8 @@ omp_declare_variant_finalize (tree decl,
     }
 }
 
+static void cp_maybe_mangle_decomp (tree, tree, unsigned int);
+
 /* Finish processing of a declaration;
    install its line number and initial value.
    If the length of an array type is not known before,
@@ -8221,11 +8225,14 @@ omp_declare_variant_finalize (tree decl,
    true, then INIT is an integral constant expression.
 
    FLAGS is LOOKUP_ONLYCONVERTING if the = init syntax was used, else 0
-   if the (init) syntax was used.  */
+   if the (init) syntax was used.
+
+   DECOMP is first identifier's DECL and identifier count in a structured
+   bindings, nullptr if not a structured binding.  */
 
 void
 cp_finish_decl (tree decl, tree init, bool init_const_expr_p,
-		tree asmspec_tree, int flags)
+		tree asmspec_tree, int flags, cp_decomp *decomp)
 {
   vec<tree, va_gc> *cleanups = NULL;
   const char *asmspec = NULL;
@@ -8600,6 +8607,9 @@ cp_finish_decl (tree decl, tree init, bo
 	  return;
 	}
 
+      if (decomp)
+	cp_maybe_mangle_decomp (decl, decomp->decl, decomp->count);
+
       /* If this is a local variable that will need a mangled name,
 	 register it now.  We must do this before processing the
 	 initializer for the variable, since the initialization might
@@ -9070,7 +9080,7 @@ lookup_decomp_type (tree v)
 /* Mangle a decomposition declaration if needed.  Arguments like
    in cp_finish_decomp.  */
 
-void
+static void
 cp_maybe_mangle_decomp (tree decl, tree first, unsigned int count)
 {
   if (!processing_template_decl
@@ -9082,6 +9092,25 @@ cp_maybe_mangle_decomp (tree decl, tree
       tree d = first;
       for (unsigned int i = 0; i < count; i++, d = DECL_CHAIN (d))
 	v[count - i - 1] = d;
+      if (DECL_FUNCTION_SCOPE_P (decl))
+	{
+	  size_t sz = 3;
+	  for (unsigned int i = 0; i < count; ++i)
+	    sz += IDENTIFIER_LENGTH (DECL_NAME (v[i])) + 1;
+	  char *name = XALLOCAVEC (char, sz);
+	  name[0] = 'D';
+	  name[1] = 'C';
+	  char *p = name + 2;
+	  for (unsigned int i = 0; i < count; ++i)
+	    {
+	      size_t len = IDENTIFIER_LENGTH (DECL_NAME (v[i]));
+	      *p++ = ' ';
+	      memcpy (p, IDENTIFIER_POINTER (DECL_NAME (v[i])), len);
+	      p += len;
+	    }
+	  *p = '\0';
+	  determine_local_discriminator (decl, get_identifier (name));
+	}
       SET_DECL_ASSEMBLER_NAME (decl, mangle_decomp (decl, v));
       maybe_apply_pragma_weak (decl);
     }
--- gcc/cp/mangle.cc.jj	2023-08-28 10:32:42.172570630 +0200
+++ gcc/cp/mangle.cc	2023-08-28 10:35:30.333342885 +0200
@@ -1347,51 +1347,6 @@ write_template_prefix (const tree node)
   add_substitution (substitution);
 }
 
-/* As the list of identifiers for the structured binding declaration
-   DECL is likely gone, try to recover the DC <source-name>+ E portion
-   from its mangled name.  Return pointer to the DC and set len to
-   the length up to and including the terminating E.  On failure
-   return NULL.  */
-
-static const char *
-find_decomp_unqualified_name (tree decl, size_t *len)
-{
-  const char *p = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl));
-  const char *end = p + IDENTIFIER_LENGTH (DECL_ASSEMBLER_NAME (decl));
-  bool nested = false;
-  if (!startswith (p, "_Z"))
-    return NULL;
-  p += 2;
-  if (startswith (p, "St"))
-    p += 2;
-  else if (*p == 'N')
-    {
-      nested = true;
-      ++p;
-      while (ISDIGIT (p[0]))
-	{
-	  char *e;
-	  long num = strtol (p, &e, 10);
-	  if (num >= 1 && num < end - e)
-	    p = e + num;
-	  else
-	    break;
-	}
-    }
-  if (!startswith (p, "DC"))
-    return NULL;
-  if (nested)
-    {
-      if (end[-1] != 'E')
-	return NULL;
-      --end;
-    }
-  if (end[-1] != 'E')
-    return NULL;
-  *len = end - p;
-  return p;
-}
-
 /* "For the purposes of mangling, the name of an anonymous union is considered
    to be the name of the first named data member found by a pre-order,
    depth-first, declaration-order walk of the data members of the anonymous
@@ -1465,17 +1420,7 @@ write_unqualified_name (tree decl)
     {
       found = true;
       gcc_assert (DECL_ASSEMBLER_NAME_SET_P (decl));
-      const char *decomp_str = NULL;
-      size_t decomp_len = 0;
-      if (VAR_P (decl)
-	  && DECL_DECOMPOSITION_P (decl)
-	  && DECL_NAME (decl) == NULL_TREE
-	  && DECL_NAMESPACE_SCOPE_P (decl))
-	decomp_str = find_decomp_unqualified_name (decl, &decomp_len);
-      if (decomp_str)
-	write_chars (decomp_str, decomp_len);
-      else
-	write_source_name (DECL_ASSEMBLER_NAME (decl));
+      write_source_name (DECL_ASSEMBLER_NAME (decl));
     }
   else if (DECL_DECLARES_FUNCTION_P (decl))
     {
@@ -4373,6 +4318,7 @@ mangle_decomp (const tree decl, vec<tree
   location_t saved_loc = input_location;
   input_location = DECL_SOURCE_LOCATION (decl);
 
+  check_abi_tags (decl);
   start_mangling (decl);
   write_string ("_Z");
 
@@ -4380,13 +4326,21 @@ mangle_decomp (const tree decl, vec<tree
   gcc_assert (context != NULL_TREE);
 
   bool nested = false;
+  bool local = false;
   if (DECL_NAMESPACE_STD_P (context))
     write_string ("St");
+  else if (TREE_CODE (context) == FUNCTION_DECL)
+    {
+      local = true;
+      write_char ('Z');
+      write_encoding (context);
+      write_char ('E');
+    }
   else if (context != global_namespace)
     {
       nested = true;
       write_char ('N');
-      write_prefix (decl_mangling_context (decl));
+      write_prefix (context);
     }
 
   write_string ("DC");
@@ -4396,8 +4350,21 @@ mangle_decomp (const tree decl, vec<tree
     write_unqualified_name (d);
   write_char ('E');
 
+  if (tree tags = get_abi_tags (decl))
+    {
+      /* We didn't emit ABI tags for structured bindings before ABI 19.  */
+      if (!G.need_abi_warning
+          && abi_warn_or_compat_version_crosses (19))
+	G.need_abi_warning = 1;
+
+      if (abi_version_at_least (19))
+	write_abi_tags (tags);
+    }
+
   if (nested)
     write_char ('E');
+  else if (local && DECL_DISCRIMINATOR_P (decl))
+    write_discriminator (discriminator_for_local_entity (decl));
 
   tree id = finish_mangling_get_identifier ();
   if (DEBUG_MANGLE)
@@ -4405,6 +4372,37 @@ mangle_decomp (const tree decl, vec<tree
              IDENTIFIER_POINTER (id));
 
   input_location = saved_loc;
+
+  if (warn_abi && G.need_abi_warning)
+    {
+      const char fabi_version[] = "-fabi-version";
+      tree id2 = id;
+      int save_ver = flag_abi_version;
+
+      if (flag_abi_version != warn_abi_version)
+	{
+	  flag_abi_version = warn_abi_version;
+	  id2 = mangle_decomp (decl, decls);
+	  flag_abi_version = save_ver;
+	}
+
+      if (id2 == id)
+	/* OK.  */;
+      else if (warn_abi_version != 0
+	       && abi_version_at_least (warn_abi_version))
+	warning_at (DECL_SOURCE_LOCATION (G.entity), OPT_Wabi,
+		    "the mangled name of %qD changed between "
+		    "%<%s=%d%> (%qD) and %<%s=%d%> (%qD)",
+		    G.entity, fabi_version, warn_abi_version, id2,
+		    fabi_version, save_ver, id);
+      else
+	warning_at (DECL_SOURCE_LOCATION (G.entity), OPT_Wabi,
+		    "the mangled name of %qD changes between "
+		    "%<%s=%d%> (%qD) and %<%s=%d%> (%qD)",
+		    G.entity, fabi_version, save_ver, id,
+		    fabi_version, warn_abi_version, id2);
+    }
+
   return id;
 }
 
@@ -4574,6 +4572,13 @@ write_guarded_var_name (const tree varia
     /* The name of a guard variable for a reference temporary should refer
        to the reference, not the temporary.  */
     write_string (IDENTIFIER_POINTER (DECL_NAME (variable)) + 4);
+  else if (DECL_DECOMPOSITION_P (variable)
+	   && DECL_NAME (variable) == NULL_TREE
+	   && startswith (IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (variable)),
+			  "_Z"))
+    /* The name of a guard variable for a structured binding needs special
+       casing.  */
+    write_string (IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (variable)) + 2);
   else
     write_name (variable, /*ignore_local_scope=*/0);
 }
@@ -4640,7 +4645,7 @@ mangle_ref_init_variable (const tree var
   start_mangling (variable);
   write_string ("_ZGR");
   check_abi_tags (variable);
-  write_name (variable, /*ignore_local_scope=*/0);
+  write_guarded_var_name (variable);
   /* Avoid name clashes with aggregate initialization of multiple
      references at once.  */
   write_compact_number (current_ref_temp_count++);
--- gcc/cp/parser.cc.jj	2023-08-28 10:33:10.985188932 +0200
+++ gcc/cp/parser.cc	2023-08-28 11:06:51.260457348 +0200
@@ -14182,15 +14182,20 @@ cp_convert_range_for (tree statement, tr
 				     tf_warning_or_error);
   finish_for_expr (expression, statement);
 
+  cp_decomp decomp_data, *decomp = nullptr;
   if (VAR_P (range_decl) && DECL_DECOMPOSITION_P (range_decl))
-    cp_maybe_mangle_decomp (range_decl, decomp_first_name, decomp_cnt);
+    {
+      decomp_data.decl = decomp_first_name;
+      decomp_data.count = decomp_cnt;
+      decomp = &decomp_data;
+    }
 
   /* The declaration is initialized with *__begin inside the loop body.  */
   tree deref_begin = build_x_indirect_ref (input_location, begin, RO_UNARY_STAR,
 					   NULL_TREE, tf_warning_or_error);
   cp_finish_decl (range_decl, deref_begin,
 		  /*is_constant_init*/false, NULL_TREE,
-		  LOOKUP_ONLYCONVERTING);
+		  LOOKUP_ONLYCONVERTING, decomp);
   if (VAR_P (range_decl) && DECL_DECOMPOSITION_P (range_decl))
     cp_finish_decomp (range_decl, decomp_first_name, decomp_cnt);
 
@@ -15890,9 +15895,10 @@ cp_parser_decomposition_declaration (cp_
 
       if (decl != error_mark_node)
 	{
-	  cp_maybe_mangle_decomp (decl, prev, v.length ());
+	  cp_decomp decomp = { prev, v.length () };
 	  cp_finish_decl (decl, initializer, non_constant_p, NULL_TREE,
-			  (is_direct_init ? LOOKUP_NORMAL : LOOKUP_IMPLICIT));
+			  (is_direct_init ? LOOKUP_NORMAL : LOOKUP_IMPLICIT),
+			  &decomp);
 	  cp_finish_decomp (decl, prev, v.length ());
 	}
     }
@@ -43697,27 +43703,26 @@ cp_finish_omp_range_for (tree orig, tree
   gcc_assert (TREE_CODE (orig) == TREE_LIST
 	      && TREE_CODE (TREE_CHAIN (orig)) == TREE_VEC);
   tree decl = TREE_VEC_ELT (TREE_CHAIN (orig), 2);
-  tree decomp_first_name = NULL_TREE;
-  unsigned int decomp_cnt = 0;
+  cp_decomp decomp_data, *decomp = nullptr;
 
   if (VAR_P (decl) && DECL_DECOMPOSITION_P (decl))
     {
-      decomp_first_name = TREE_VEC_ELT (TREE_CHAIN (orig), 3);
-      decomp_cnt = TREE_VEC_LENGTH (TREE_CHAIN (orig)) - 3;
+      decomp_data.decl = TREE_VEC_ELT (TREE_CHAIN (orig), 3);
+      decomp_data.count = TREE_VEC_LENGTH (TREE_CHAIN (orig)) - 3;
       if (TREE_PUBLIC (TREE_CHAIN (orig)))
 	{
 	  /* Undo temporary clearing of DECL_HAS_VALUE_EXPR_P done
 	     by cp_convert_omp_range_for above.  */
 	  TREE_PUBLIC (TREE_CHAIN (orig)) = 0;
-	  tree d = decomp_first_name;
-	  for (unsigned i = 0; i < decomp_cnt; i++)
+	  tree d = decomp_data.decl;
+	  for (unsigned i = 0; i < decomp_data.count; i++)
 	    {
 	      if (TREE_TYPE (d) != error_mark_node)
 		DECL_HAS_VALUE_EXPR_P (d) = 1;
 	      d = DECL_CHAIN (d);
 	    }
 	}
-      cp_maybe_mangle_decomp (decl, decomp_first_name, decomp_cnt);
+      decomp = &decomp_data;
     }
 
   /* The declaration is initialized with *__begin inside the loop body.  */
@@ -43725,9 +43730,9 @@ cp_finish_omp_range_for (tree orig, tree
 		  build_x_indirect_ref (input_location, begin, RO_UNARY_STAR,
 					NULL_TREE, tf_warning_or_error),
 		  /*is_constant_init*/false, NULL_TREE,
-		  LOOKUP_ONLYCONVERTING);
+		  LOOKUP_ONLYCONVERTING, decomp);
   if (VAR_P (decl) && DECL_DECOMPOSITION_P (decl))
-    cp_finish_decomp (decl, decomp_first_name, decomp_cnt);
+    cp_finish_decomp (decl, decomp->decl, decomp->count);
 }
 
 /* Return true if next tokens contain a standard attribute that contains
--- gcc/cp/pt.cc.jj	2023-08-28 10:33:11.045188137 +0200
+++ gcc/cp/pt.cc	2023-08-28 10:49:36.880140901 +0200
@@ -19043,8 +19043,8 @@ tsubst_expr (tree t, tree args, tsubst_f
 		else
 		  {
 		    bool const_init = false;
-		    unsigned int cnt = 0;
-		    tree first = NULL_TREE, ndecl = error_mark_node;
+		    cp_decomp decomp_data, *decomp = nullptr;
+		    tree ndecl = error_mark_node;
 		    tree asmspec_tree = NULL_TREE;
 		    maybe_push_decl (decl);
 
@@ -19056,9 +19056,13 @@ tsubst_expr (tree t, tree args, tsubst_f
 		    if (VAR_P (decl)
 			&& DECL_DECOMPOSITION_P (decl)
 			&& TREE_TYPE (pattern_decl) != error_mark_node)
-		      ndecl = tsubst_decomp_names (decl, pattern_decl, args,
-						   complain, in_decl, &first,
-						   &cnt);
+		      {
+			ndecl = tsubst_decomp_names (decl, pattern_decl, args,
+						     complain, in_decl,
+						     &decomp_data.decl,
+						     &decomp_data.count);
+			decomp = &decomp_data;
+		      }
 
 		    init = tsubst_init (init, decl, args, complain, in_decl);
 
@@ -19066,9 +19070,6 @@ tsubst_expr (tree t, tree args, tsubst_f
 		      const_init = (DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P
 				    (pattern_decl));
 
-		    if (ndecl != error_mark_node)
-		      cp_maybe_mangle_decomp (ndecl, first, cnt);
-
 		    /* In a non-template function, VLA type declarations are
 		       handled in grokdeclarator; for templates, handle them
 		       now.  */
@@ -19085,10 +19086,11 @@ tsubst_expr (tree t, tree args, tsubst_f
 			TREE_TYPE (asmspec_tree) = char_array_type_node;
 		      }
 
-		    cp_finish_decl (decl, init, const_init, asmspec_tree, 0);
+		    cp_finish_decl (decl, init, const_init, asmspec_tree, 0,
+				    decomp);
 
 		    if (ndecl != error_mark_node)
-		      cp_finish_decomp (ndecl, first, cnt);
+		      cp_finish_decomp (ndecl, decomp->decl, decomp->count);
 		  }
 	      }
 	  }
--- gcc/testsuite/g++.dg/cpp2a/decomp8.C.jj	2023-08-28 10:35:30.337342832 +0200
+++ gcc/testsuite/g++.dg/cpp2a/decomp8.C	2023-08-28 12:42:30.747030644 +0200
@@ -0,0 +1,74 @@
+// PR c++/111069
+// { dg-do compile { target c++11 } }
+// { dg-options "" }
+
+extern int a[2];
+struct Y { int b, c, d; };
+
+inline int
+freddy ()
+{
+  static auto [i, j] = a;		// { dg-warning "structured bindings only available with" "" { target c++14_down } }
+					// { dg-warning "structured binding declaration can be 'static' only in" "" { target c++17_down } .-1 }
+  static auto [k, l] = a;		// { dg-warning "structured bindings only available with" "" { target c++14_down } }
+					// { dg-warning "structured binding declaration can be 'static' only in" "" { target c++17_down } .-1 }
+  int ret = ++i + ++k;
+  {
+    static auto [i, j] = a;		// { dg-warning "structured bindings only available with" "" { target c++14_down } }
+					// { dg-warning "structured binding declaration can be 'static' only in" "" { target c++17_down } .-1 }
+    static auto [k, l] = a;		// { dg-warning "structured bindings only available with" "" { target c++14_down } }
+					// { dg-warning "structured binding declaration can be 'static' only in" "" { target c++17_down } .-1 }
+    ret += ++i + ++k;
+  }
+  {
+    static auto [i, j] = a;		// { dg-warning "structured bindings only available with" "" { target c++14_down } }
+					// { dg-warning "structured binding declaration can be 'static' only in" "" { target c++17_down } .-1 }
+    static auto [k, l] = a;		// { dg-warning "structured bindings only available with" "" { target c++14_down } }
+					// { dg-warning "structured binding declaration can be 'static' only in" "" { target c++17_down } .-1 }
+    ret += ++i + ++k;
+  }
+  return ret;
+}
+
+namespace N
+{
+  namespace M
+  {
+    template <int N>
+    inline int
+    corge ()
+    {
+      static auto [i, j] = a;		// { dg-warning "structured bindings only available with" "" { target c++14_down } }
+					// { dg-warning "structured binding declaration can be 'static' only in" "" { target c++17_down } .-1 }
+      static auto && [u, v, w] = Y{};	// { dg-warning "structured bindings only available with" "" { target c++14_down } }
+					// { dg-warning "structured binding declaration can be 'static' only in" "" { target c++17_down } .-1 }
+      int ret = ++i + ++u;
+      {
+	static auto && [u, v, w] = Y{};	// { dg-warning "structured bindings only available with" "" { target c++14_down } }
+					// { dg-warning "structured binding declaration can be 'static' only in" "" { target c++17_down } .-1 }
+	ret += ++v;
+      }
+      return ret;
+    }
+  }
+}
+
+int (*p) () = &freddy;
+int (*q) () = N::M::corge<3>;
+
+// { dg-final { scan-assembler "_ZZ6freddyvEDC1i1jE" } }
+// { dg-final { scan-assembler "_ZZ6freddyvEDC1i1jE_0" } }
+// { dg-final { scan-assembler "_ZZ6freddyvEDC1i1jE_1" } }
+// { dg-final { scan-assembler "_ZZ6freddyvEDC1k1lE" } }
+// { dg-final { scan-assembler "_ZZ6freddyvEDC1k1lE_0" } }
+// { dg-final { scan-assembler "_ZZ6freddyvEDC1k1lE_1" } }
+// { dg-final { scan-assembler "_ZZN1N1M5corgeILi3EEEivEDC1i1jE" } }
+// { dg-final { scan-assembler "_ZGVZ6freddyvEDC1i1jE" } }
+// { dg-final { scan-assembler "_ZGVZ6freddyvEDC1i1jE_0" } }
+// { dg-final { scan-assembler "_ZGVZ6freddyvEDC1i1jE_1" } }
+// { dg-final { scan-assembler "_ZGVZ6freddyvEDC1k1lE" } }
+// { dg-final { scan-assembler "_ZGVZ6freddyvEDC1k1lE_0" } }
+// { dg-final { scan-assembler "_ZGVZ6freddyvEDC1k1lE_1" } }
+// { dg-final { scan-assembler "_ZGVZN1N1M5corgeILi3EEEivEDC1i1jE" } }
+// { dg-final { scan-assembler "_ZGRZN1N1M5corgeILi3EEEivEDC1u1v1wE_" } }
+// { dg-final { scan-assembler "_ZGRZN1N1M5corgeILi3EEEivEDC1u1v1wE_0_" } }
--- gcc/testsuite/g++.dg/cpp2a/decomp9.C.jj	2023-08-28 12:21:50.292919000 +0200
+++ gcc/testsuite/g++.dg/cpp2a/decomp9.C	2023-08-28 12:33:25.478452184 +0200
@@ -0,0 +1,82 @@
+// PR c++/111069
+// { dg-do compile { target c++11 } }
+// { dg-options "" }
+
+struct [[gnu::abi_tag ("foobar")]] S { int i; };
+extern S a[2];
+struct [[gnu::abi_tag ("qux")]] T { int i; S j; int k; };
+extern T b[2];
+
+namespace N {
+  auto [i, j] = a;		// { dg-warning "structured bindings only available with" "" { target c++14_down } }
+  auto [k, l] = b;		// { dg-warning "structured bindings only available with" "" { target c++14_down } }
+}
+
+inline int
+foo ()
+{
+  static auto [m, n] = a;	// { dg-warning "structured bindings only available with" "" { target c++14_down } }
+				// { dg-warning "structured binding declaration can be 'static' only in" "" { target c++17_down } .-1 }
+  static auto [o, p] = b;	// { dg-warning "structured bindings only available with" "" { target c++14_down } }
+				// { dg-warning "structured binding declaration can be 'static' only in" "" { target c++17_down } .-1 }
+  int ret = ++N::i.i + ++N::k.i;
+  {
+    static auto [m, n] = a;	// { dg-warning "structured bindings only available with" "" { target c++14_down } }
+				// { dg-warning "structured binding declaration can be 'static' only in" "" { target c++17_down } .-1 }
+
+    ret += ++n.i;
+  }
+  {
+    static auto [m, n] = a;	// { dg-warning "structured bindings only available with" "" { target c++14_down } }
+				// { dg-warning "structured binding declaration can be 'static' only in" "" { target c++17_down } .-1 }
+
+    ret += ++n.i;
+  }
+  ret += ++m.i + ++o.i;
+  return ret;
+}
+
+template <typename T>
+inline int
+bar ()
+{
+  static auto [m, n] = a;	// { dg-warning "structured bindings only available with" "" { target c++14_down } }
+				// { dg-warning "structured binding declaration can be 'static' only in" "" { target c++17_down } .-1 }
+  static auto [o, p] = b;	// { dg-warning "structured bindings only available with" "" { target c++14_down } }
+				// { dg-warning "structured binding declaration can be 'static' only in" "" { target c++17_down } .-1 }
+  int ret = 0;
+  {
+    static auto [m, n] = a;	// { dg-warning "structured bindings only available with" "" { target c++14_down } }
+				// { dg-warning "structured binding declaration can be 'static' only in" "" { target c++17_down } .-1 }
+    ret += ++n.i;
+  }
+  {
+    static auto [m, n] = a;	// { dg-warning "structured bindings only available with" "" { target c++14_down } }
+				// { dg-warning "structured binding declaration can be 'static' only in" "" { target c++17_down } .-1 }
+    ret += ++n.i;
+  }
+  ret += ++m.i + ++o.i;
+  return ret;
+}
+
+int (*p) () = &foo;
+int (*q) () = &bar<T>;
+
+// { dg-final { scan-assembler "_ZZ3foovEDC1m1nEB6foobar" } }
+// { dg-final { scan-assembler "_ZZ3foovEDC1m1nEB6foobar_0" } }
+// { dg-final { scan-assembler "_ZZ3foovEDC1m1nEB6foobar_1" } }
+// { dg-final { scan-assembler "_ZZ3foovEDC1o1pEB3qux" } }
+// { dg-final { scan-assembler "_ZZ3barI1TB3quxEivEDC1m1nEB6foobar" } }
+// { dg-final { scan-assembler "_ZZ3barI1TB3quxEivEDC1m1nEB6foobar_0" } }
+// { dg-final { scan-assembler "_ZZ3barI1TB3quxEivEDC1m1nEB6foobar_1" } }
+// { dg-final { scan-assembler "_ZZ3barI1TB3quxEivEDC1o1pEB3qux" } }
+// { dg-final { scan-assembler "_ZN1NDC1i1jEB6foobarE" } }
+// { dg-final { scan-assembler "_ZN1NDC1k1lEB3quxE" } }
+// { dg-final { scan-assembler "_ZGVZ3foovEDC1m1nEB6foobar" } }
+// { dg-final { scan-assembler "_ZGVZ3foovEDC1m1nEB6foobar_0" } }
+// { dg-final { scan-assembler "_ZGVZ3foovEDC1m1nEB6foobar_1" } }
+// { dg-final { scan-assembler "_ZGVZ3foovEDC1o1pEB3qux" } }
+// { dg-final { scan-assembler "_ZGVZ3barI1TB3quxEivEDC1m1nEB6foobar" } }
+// { dg-final { scan-assembler "_ZGVZ3barI1TB3quxEivEDC1m1nEB6foobar_0" } }
+// { dg-final { scan-assembler "_ZGVZ3barI1TB3quxEivEDC1m1nEB6foobar_1" } }
+// { dg-final { scan-assembler "_ZGVZ3barI1TB3quxEivEDC1o1pEB3qux" } }
--- gcc/testsuite/g++.dg/abi/macro0.C.jj	2022-10-11 12:10:42.209890842 +0200
+++ gcc/testsuite/g++.dg/abi/macro0.C	2023-08-28 15:46:51.205462641 +0200
@@ -1,6 +1,6 @@
 // This testcase will need to be kept in sync with c_common_post_options.
 // { dg-options "-fabi-version=0" }
 
-#if __GXX_ABI_VERSION != 1018
+#if __GXX_ABI_VERSION != 1019
 #error "Incorrect value of __GXX_ABI_VERSION"
 #endif



	Jakub


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

* Re: [PATCH] c++, v2: Fix up mangling of function/block scope static structured bindings and emit abi tags [PR111069]
  2023-08-28 13:58     ` [PATCH] c++, v2: Fix up mangling of function/block scope static structured bindings and emit abi tags [PR111069] Jakub Jelinek
@ 2023-08-31 17:11       ` Jason Merrill
  2023-08-31 19:14         ` [PATCH] c++, v3: " Jakub Jelinek
  0 siblings, 1 reply; 7+ messages in thread
From: Jason Merrill @ 2023-08-31 17:11 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: gcc-patches

On 8/28/23 09:58, Jakub Jelinek wrote:
> Hi!
> 
> On Thu, Aug 24, 2023 at 06:39:10PM +0200, Jakub Jelinek via Gcc-patches wrote:
>>> Maybe do this in mangle_decomp, based on the actual mangling in process
>>> instead of this pseudo-mangling?
>>
>> Not sure that is possible, for 2 reasons:
>> 1) determine_local_discriminator otherwise works on DECL_NAME, not mangled
>>     names, so if one uses (albeit implementation reserved)
>>     _ZZN1N3fooI1TB3bazEEivEDC1h1iEB6foobar and similar identifiers, they
>>     could clash with the counting of the structured bindings

I guess, but those names are reserved so that we don't need to worry 
about that.

>> 2) seems the local discriminator counting shouldn't take into account
>>     details like abi tags, e.g. if I have:

Right, you'd need to use the partial mangled name before the ABI tags, 
e.g. with get_identifier_with_length (obstack_base, obstack_object_size).

But the way you have it is fine too.

> The following updated patch handles everything except it leaves for the
> above 2 reasons the determination of local discriminator where it was.
> I had to add a new (defaulted) argument to cp_finish_decl and do
> cp_maybe_mangle_decomp from there, so that it is after e.g. auto type
> deduction and maybe_commonize_var (which had to be changed as well) and
> spots in cp_finish_decl where we need or might need mangled names already.
> 
> Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?
> 
> There is one difference between g++ with this patch and clang++,
> g++ uses
> _ZZ3barI1TB3quxEivEDC1o1pEB3qux
> while clang++ uses
> _ZZ3barI1TB3quxEivEDC1o1pE
> but from what I can see, such a difference is there also when just using
> normal local decls:
> struct [[gnu::abi_tag ("foobar")]] S { int i; };
> struct [[gnu::abi_tag ("qux")]] T { int i; S j; int k; };
> 
> inline int
> foo ()
> {
>    static S c;
>    static T d;
>    return ++c.i + ++d.i;
> }
> 
> template <typename T>
> inline int
> bar ()
> {
>    static S c;
>    static T d;
>    return ++c.i + ++d.i;
> }
> 
> int (*p) () = &foo;
> int (*q) () = &bar<T>;
> where both compilers mangle c in foo as:
> _ZZ3foovE1cB6foobar
> and d in there as
> _ZZ3foovE1dB3qux
> and similarly both compilers mangle c in bar as
> _ZZ3barI1TB3quxEivE1cB6foobar
> but g++ mangles d in bar as
> _ZZ3barI1TB3quxEivE1dB3qux
> while clang++ mangles it as just
> _ZZ3barI1TB3quxEivE1d
> No idea what is right or wrong according to Itanium mangling.

I think g++ is right.

This has to do with the ABI "If part of a declaration's type is not 
represented in the mangling, i.e. the type of a variable or a return 
type that is not represented in the mangling of a function, any ABI tags 
on that type (or components of a compound type) that are not also 
present in a mangled part of the type are applied to the name of the 
declaration."

Here the type of bar::d is not mangled, so the tags of T are applied to 
the name d.

I guess clang interpreted the above as "any ABI tags on that type that 
are not already present in the mangled name...", which would also be a 
reasonable rule but is not the actual rule in the ABI.

> 2023-08-28  Jakub Jelinek  <jakub@redhat.com>
> 
> 	PR c++/111069
> gcc/
> 	* common.opt (fabi-version=): Document version 19.
> 	* doc/invoke.texi (-fabi-version=): Likewise.
> gcc/c-family/
> 	* c-opts.cc (c_common_post_options): Change latest_abi_version to 19.
> gcc/cp/
> 	* cp-tree.h (determine_local_discriminator): Add NAME argument with
> 	NULL_TREE default.
> 	(struct cp_decomp): New type.

Maybe cp_finish_decomp should take this as well?  And 
tsubst_decomp_names, and various other functions with 
decomp_first_name/decomp_cnt parms?

> +  if (tree tags = get_abi_tags (decl))
> +    {
> +      /* We didn't emit ABI tags for structured bindings before ABI 19.  */
> +      if (!G.need_abi_warning
> +          && abi_warn_or_compat_version_crosses (19))
> +	G.need_abi_warning = 1;

In general we should probably only warn about mangling changes if 
TREE_PUBLIC (decl).

Jason


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

* [PATCH] c++, v3: Fix up mangling of function/block scope static structured bindings and emit abi tags [PR111069]
  2023-08-31 17:11       ` Jason Merrill
@ 2023-08-31 19:14         ` Jakub Jelinek
  2023-08-31 19:41           ` Jason Merrill
  0 siblings, 1 reply; 7+ messages in thread
From: Jakub Jelinek @ 2023-08-31 19:14 UTC (permalink / raw)
  To: Jason Merrill; +Cc: gcc-patches

On Thu, Aug 31, 2023 at 01:11:57PM -0400, Jason Merrill wrote:
> > 2023-08-28  Jakub Jelinek  <jakub@redhat.com>
> > 
> > 	PR c++/111069
> > gcc/
> > 	* common.opt (fabi-version=): Document version 19.
> > 	* doc/invoke.texi (-fabi-version=): Likewise.
> > gcc/c-family/
> > 	* c-opts.cc (c_common_post_options): Change latest_abi_version to 19.
> > gcc/cp/
> > 	* cp-tree.h (determine_local_discriminator): Add NAME argument with
> > 	NULL_TREE default.
> > 	(struct cp_decomp): New type.
> 
> Maybe cp_finish_decomp should take this as well?  And tsubst_decomp_names,
> and various other functions with decomp_first_name/decomp_cnt parms?

Ok, done below.

> > +  if (tree tags = get_abi_tags (decl))
> > +    {
> > +      /* We didn't emit ABI tags for structured bindings before ABI 19.  */
> > +      if (!G.need_abi_warning
> > +          && abi_warn_or_compat_version_crosses (19))
> > +	G.need_abi_warning = 1;
> 
> In general we should probably only warn about mangling changes if
> TREE_PUBLIC (decl).

I have done that but I think it ought to be unnecessary, because
check_abi_tags starts with
  if (!TREE_PUBLIC (decl))
    /* No need to worry about things local to this TU.  */
    return NULL_TREE;

Here is an updated patch, so far just tested with
make check-g++ GXX_TESTSUITE_STDS=98,11,14,17,20,2b,2c RUNTESTFLAGS="dg.exp='*decomp*'"
ok for trunk if it passes full bootstrap/regtest?

2023-08-31  Jakub Jelinek  <jakub@redhat.com>

	PR c++/111069
gcc/
	* common.opt (fabi-version=): Document version 19.
	* doc/invoke.texi (-fabi-version=): Likewise.
gcc/c-family/
	* c-opts.cc (c_common_post_options): Change latest_abi_version to 19.
gcc/cp/
	* cp-tree.h (determine_local_discriminator): Add NAME argument with
	NULL_TREE default.
	(struct cp_decomp): New type.
	(cp_finish_decl): Add DECOMP argument defaulted to nullptr.
	(cp_maybe_mangle_decomp): Remove declaration.
	(cp_finish_decomp): Add cp_decomp * argument, remove tree and unsigned
	args.
	(cp_convert_range_for): Likewise.
	* decl.cc (determine_local_discriminator): Add NAME argument, use it
	if non-NULL, otherwise compute it the old way.
	(maybe_commonize_var): Don't return early for structured bindings.
	(cp_finish_decl): Add DECOMP argument, if non-NULL, call
	cp_maybe_mangle_decomp.
	(cp_maybe_mangle_decomp): Make it static with a forward declaration.
	Call determine_local_discriminator.  Replace FIRST and COUNT arguments
	with DECOMP argument.
	(cp_finish_decomp): Replace FIRST and COUNT arguments with DECOMP
	argument.
	* mangle.cc (find_decomp_unqualified_name): Remove.
	(write_unqualified_name): Don't call find_decomp_unqualified_name.
	(mangle_decomp): Handle mangling of static function/block scope
	structured bindings.  Don't call decl_mangling_context twice.  Call
	check_abi_tags, call write_abi_tags for abi version >= 19 and emit
	-Wabi warnings if needed.
	(write_guarded_var_name): Handle structured bindings.
	(mangle_ref_init_variable): Use write_guarded_var_name.
	* parser.cc (cp_parser_range_for): Adjust do_range_for_auto_deduction
	and cp_convert_range_for callers.
	(do_range_for_auto_deduction): Replace DECOMP_FIRST_NAME and
	DECOMP_CNT arguments with DECOMP.  Adjust cp_finish_decomp caller.
	(cp_convert_range_for): Replace DECOMP_FIRST_NAME and
	DECOMP_CNT arguments with DECOMP.  Don't call cp_maybe_mangle_decomp,
	adjust cp_finish_decl and cp_finish_decomp callers.
	(cp_parser_decomposition_declaration): Don't call
	cp_maybe_mangle_decomp, adjust cp_finish_decl and cp_finish_decomp
	callers.
	(cp_convert_omp_range_for): Adjust do_range_for_auto_deduction
	and cp_finish_decomp callers.
	(cp_finish_omp_range_for): Don't call cp_maybe_mangle_decomp,
	adjust cp_finish_decl and cp_finish_decomp callers.
	* pt.cc (tsubst_omp_for_iterator): Adjust tsubst_decomp_names
	caller.
	(tsubst_decomp_names): Replace FIRST and CNT arguments with DECOMP.
	(tsubst_expr): Don't call cp_maybe_mangle_decomp, adjust
	tsubst_decomp_names, cp_finish_decl, cp_finish_decomp and
	cp_convert_range_for callers.
gcc/testsuite/
	* g++.dg/cpp2a/decomp8.C: New test.
	* g++.dg/cpp2a/decomp9.C: New test.
	* g++.dg/abi/macro0.C: Expect __GXX_ABI_VERSION 1019 rather than
	1018.

--- gcc/common.opt.jj	2023-08-28 13:55:55.670370386 +0200
+++ gcc/common.opt	2023-08-31 19:53:31.186280641 +0200
@@ -1010,6 +1010,9 @@ Driver Undocumented
 ; 18: Corrects errors in mangling of lambdas with additional context.
 ;     Default in G++ 13.
 ;
+; 19: Emits ABI tags if needed in structured binding mangled names.
+;     Default in G++ 14.
+;
 ; Additional positive integers will be assigned as new versions of
 ; the ABI become the default version of the ABI.
 fabi-version=
--- gcc/doc/invoke.texi.jj	2023-08-31 19:52:16.734307207 +0200
+++ gcc/doc/invoke.texi	2023-08-31 19:53:31.191280572 +0200
@@ -3017,6 +3017,9 @@ in C++14 and up.
 Version 18, which first appeard in G++ 13, fixes manglings of lambdas
 that have additional context.
 
+Version 19, which first appeard in G++ 14, fixes manglings of structured
+bindings to include ABI tags.
+
 See also @option{-Wabi}.
 
 @opindex fabi-compat-version
--- gcc/c-family/c-opts.cc.jj	2023-08-28 13:55:55.613371156 +0200
+++ gcc/c-family/c-opts.cc	2023-08-31 19:53:31.192280558 +0200
@@ -974,7 +974,7 @@ c_common_post_options (const char **pfil
 
   /* Change flag_abi_version to be the actual current ABI level, for the
      benefit of c_cpp_builtins, and to make comparison simpler.  */
-  const int latest_abi_version = 18;
+  const int latest_abi_version = 19;
   /* Generate compatibility aliases for ABI v13 (8.2) by default.  */
   const int abi_compat_default = 13;
 
--- gcc/cp/cp-tree.h.jj	2023-08-31 14:31:05.991763184 +0200
+++ gcc/cp/cp-tree.h	2023-08-31 20:39:34.052154029 +0200
@@ -6859,7 +6859,7 @@ extern void pop_switch				(void);
 extern void note_break_stmt			(void);
 extern bool note_iteration_stmt_body_start	(void);
 extern void note_iteration_stmt_body_end	(bool);
-extern void determine_local_discriminator	(tree);
+extern void determine_local_discriminator	(tree, tree = NULL_TREE);
 extern bool member_like_constrained_friend_p	(tree);
 extern bool fns_correspond			(tree, tree);
 extern int decls_match				(tree, tree, bool = true);
@@ -6892,10 +6892,10 @@ extern tree start_decl				(const cp_decl
 extern void start_decl_1			(tree, bool);
 extern bool check_array_initializer		(tree, tree, tree);
 extern void omp_declare_variant_finalize	(tree, tree);
-extern void cp_finish_decl			(tree, tree, bool, tree, int);
+struct cp_decomp { tree decl; unsigned int count; };
+extern void cp_finish_decl			(tree, tree, bool, tree, int, cp_decomp * = nullptr);
 extern tree lookup_decomp_type			(tree);
-extern void cp_maybe_mangle_decomp		(tree, tree, unsigned int);
-extern void cp_finish_decomp			(tree, tree, unsigned int);
+extern void cp_finish_decomp			(tree, cp_decomp *);
 extern int cp_complete_array_type		(tree *, tree, bool);
 extern int cp_complete_array_type_or_error	(tree *, tree, bool, tsubst_flags_t);
 extern tree build_ptrmemfunc_type		(tree);
@@ -7312,7 +7312,7 @@ extern tree clone_attrs				(tree);
 extern bool maybe_clone_body			(tree);
 
 /* In parser.cc */
-extern tree cp_convert_range_for (tree, tree, tree, tree, unsigned int, bool,
+extern tree cp_convert_range_for (tree, tree, tree, cp_decomp *, bool,
 				  unsigned short, bool);
 extern void cp_convert_omp_range_for (tree &, tree &, tree &,
 				      tree &, tree &, tree &, tree &, tree &);
--- gcc/cp/decl.cc.jj	2023-08-30 18:48:48.831597950 +0200
+++ gcc/cp/decl.cc	2023-08-31 20:48:21.127722180 +0200
@@ -911,15 +911,16 @@ static GTY((deletable)) vec<tree, va_gc>
    generally very few of these in any particular function.  */
 
 void
-determine_local_discriminator (tree decl)
+determine_local_discriminator (tree decl, tree name)
 {
   auto_cond_timevar tv (TV_NAME_LOOKUP);
   retrofit_lang_decl (decl);
   tree ctx = DECL_CONTEXT (decl);
-  tree name = (TREE_CODE (decl) == TYPE_DECL
-	       && TYPE_UNNAMED_P (TREE_TYPE (decl))
-	       ? NULL_TREE : DECL_NAME (decl));
   size_t nelts = vec_safe_length (local_entities);
+  if (name == NULL_TREE)
+    name = (TREE_CODE (decl) == TYPE_DECL
+	    && TYPE_UNNAMED_P (TREE_TYPE (decl))
+	    ? NULL_TREE : DECL_NAME (decl));
   for (size_t i = 0; i < nelts; i += 2)
     {
       tree *pair = &(*local_entities)[i];
@@ -6417,8 +6418,9 @@ layout_var_decl (tree decl)
 void
 maybe_commonize_var (tree decl)
 {
-  /* Don't mess with __FUNCTION__ and similar.  */
-  if (DECL_ARTIFICIAL (decl))
+  /* Don't mess with __FUNCTION__ and similar.  But do handle structured
+     bindings.  */
+  if (DECL_ARTIFICIAL (decl) && !DECL_DECOMPOSITION_P (decl))
     return;
 
   /* Static data in a function with comdat linkage also has comdat
@@ -8212,6 +8214,8 @@ omp_declare_variant_finalize (tree decl,
     }
 }
 
+static void cp_maybe_mangle_decomp (tree, cp_decomp *);
+
 /* Finish processing of a declaration;
    install its line number and initial value.
    If the length of an array type is not known before,
@@ -8221,11 +8225,14 @@ omp_declare_variant_finalize (tree decl,
    true, then INIT is an integral constant expression.
 
    FLAGS is LOOKUP_ONLYCONVERTING if the = init syntax was used, else 0
-   if the (init) syntax was used.  */
+   if the (init) syntax was used.
+
+   DECOMP is first identifier's DECL and identifier count in a structured
+   bindings, nullptr if not a structured binding.  */
 
 void
 cp_finish_decl (tree decl, tree init, bool init_const_expr_p,
-		tree asmspec_tree, int flags)
+		tree asmspec_tree, int flags, cp_decomp *decomp)
 {
   vec<tree, va_gc> *cleanups = NULL;
   const char *asmspec = NULL;
@@ -8600,6 +8607,9 @@ cp_finish_decl (tree decl, tree init, bo
 	  return;
 	}
 
+      if (decomp)
+	cp_maybe_mangle_decomp (decl, decomp);
+
       /* If this is a local variable that will need a mangled name,
 	 register it now.  We must do this before processing the
 	 initializer for the variable, since the initialization might
@@ -9070,18 +9080,37 @@ lookup_decomp_type (tree v)
 /* Mangle a decomposition declaration if needed.  Arguments like
    in cp_finish_decomp.  */
 
-void
-cp_maybe_mangle_decomp (tree decl, tree first, unsigned int count)
+static void
+cp_maybe_mangle_decomp (tree decl, cp_decomp *decomp)
 {
   if (!processing_template_decl
       && !error_operand_p (decl)
       && TREE_STATIC (decl))
     {
       auto_vec<tree, 16> v;
-      v.safe_grow (count, true);
-      tree d = first;
-      for (unsigned int i = 0; i < count; i++, d = DECL_CHAIN (d))
-	v[count - i - 1] = d;
+      v.safe_grow (decomp->count, true);
+      tree d = decomp->decl;
+      for (unsigned int i = 0; i < decomp->count; i++, d = DECL_CHAIN (d))
+	v[decomp->count - i - 1] = d;
+      if (DECL_FUNCTION_SCOPE_P (decl))
+	{
+	  size_t sz = 3;
+	  for (unsigned int i = 0; i < decomp->count; ++i)
+	    sz += IDENTIFIER_LENGTH (DECL_NAME (v[i])) + 1;
+	  char *name = XALLOCAVEC (char, sz);
+	  name[0] = 'D';
+	  name[1] = 'C';
+	  char *p = name + 2;
+	  for (unsigned int i = 0; i < decomp->count; ++i)
+	    {
+	      size_t len = IDENTIFIER_LENGTH (DECL_NAME (v[i]));
+	      *p++ = ' ';
+	      memcpy (p, IDENTIFIER_POINTER (DECL_NAME (v[i])), len);
+	      p += len;
+	    }
+	  *p = '\0';
+	  determine_local_discriminator (decl, get_identifier (name));
+	}
       SET_DECL_ASSEMBLER_NAME (decl, mangle_decomp (decl, v));
       maybe_apply_pragma_weak (decl);
     }
@@ -9093,8 +9122,10 @@ cp_maybe_mangle_decomp (tree decl, tree
    those decls.  */
 
 void
-cp_finish_decomp (tree decl, tree first, unsigned int count)
+cp_finish_decomp (tree decl, cp_decomp *decomp)
 {
+  tree first = decomp->decl;
+  unsigned count = decomp->count;
   if (error_operand_p (decl))
     {
      error_out:
--- gcc/cp/mangle.cc.jj	2023-08-28 13:55:55.819368372 +0200
+++ gcc/cp/mangle.cc	2023-08-31 20:48:21.127722180 +0200
@@ -1347,51 +1347,6 @@ write_template_prefix (const tree node)
   add_substitution (substitution);
 }
 
-/* As the list of identifiers for the structured binding declaration
-   DECL is likely gone, try to recover the DC <source-name>+ E portion
-   from its mangled name.  Return pointer to the DC and set len to
-   the length up to and including the terminating E.  On failure
-   return NULL.  */
-
-static const char *
-find_decomp_unqualified_name (tree decl, size_t *len)
-{
-  const char *p = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl));
-  const char *end = p + IDENTIFIER_LENGTH (DECL_ASSEMBLER_NAME (decl));
-  bool nested = false;
-  if (!startswith (p, "_Z"))
-    return NULL;
-  p += 2;
-  if (startswith (p, "St"))
-    p += 2;
-  else if (*p == 'N')
-    {
-      nested = true;
-      ++p;
-      while (ISDIGIT (p[0]))
-	{
-	  char *e;
-	  long num = strtol (p, &e, 10);
-	  if (num >= 1 && num < end - e)
-	    p = e + num;
-	  else
-	    break;
-	}
-    }
-  if (!startswith (p, "DC"))
-    return NULL;
-  if (nested)
-    {
-      if (end[-1] != 'E')
-	return NULL;
-      --end;
-    }
-  if (end[-1] != 'E')
-    return NULL;
-  *len = end - p;
-  return p;
-}
-
 /* "For the purposes of mangling, the name of an anonymous union is considered
    to be the name of the first named data member found by a pre-order,
    depth-first, declaration-order walk of the data members of the anonymous
@@ -1465,17 +1420,7 @@ write_unqualified_name (tree decl)
     {
       found = true;
       gcc_assert (DECL_ASSEMBLER_NAME_SET_P (decl));
-      const char *decomp_str = NULL;
-      size_t decomp_len = 0;
-      if (VAR_P (decl)
-	  && DECL_DECOMPOSITION_P (decl)
-	  && DECL_NAME (decl) == NULL_TREE
-	  && DECL_NAMESPACE_SCOPE_P (decl))
-	decomp_str = find_decomp_unqualified_name (decl, &decomp_len);
-      if (decomp_str)
-	write_chars (decomp_str, decomp_len);
-      else
-	write_source_name (DECL_ASSEMBLER_NAME (decl));
+      write_source_name (DECL_ASSEMBLER_NAME (decl));
     }
   else if (DECL_DECLARES_FUNCTION_P (decl))
     {
@@ -4373,6 +4318,7 @@ mangle_decomp (const tree decl, vec<tree
   location_t saved_loc = input_location;
   input_location = DECL_SOURCE_LOCATION (decl);
 
+  check_abi_tags (decl);
   start_mangling (decl);
   write_string ("_Z");
 
@@ -4380,13 +4326,21 @@ mangle_decomp (const tree decl, vec<tree
   gcc_assert (context != NULL_TREE);
 
   bool nested = false;
+  bool local = false;
   if (DECL_NAMESPACE_STD_P (context))
     write_string ("St");
+  else if (TREE_CODE (context) == FUNCTION_DECL)
+    {
+      local = true;
+      write_char ('Z');
+      write_encoding (context);
+      write_char ('E');
+    }
   else if (context != global_namespace)
     {
       nested = true;
       write_char ('N');
-      write_prefix (decl_mangling_context (decl));
+      write_prefix (context);
     }
 
   write_string ("DC");
@@ -4396,8 +4350,22 @@ mangle_decomp (const tree decl, vec<tree
     write_unqualified_name (d);
   write_char ('E');
 
+  if (tree tags = get_abi_tags (decl))
+    {
+      /* We didn't emit ABI tags for structured bindings before ABI 19.  */
+      if (!G.need_abi_warning
+	  && TREE_PUBLIC (decl)
+	  && abi_warn_or_compat_version_crosses (19))
+	G.need_abi_warning = 1;
+
+      if (abi_version_at_least (19))
+	write_abi_tags (tags);
+    }
+
   if (nested)
     write_char ('E');
+  else if (local && DECL_DISCRIMINATOR_P (decl))
+    write_discriminator (discriminator_for_local_entity (decl));
 
   tree id = finish_mangling_get_identifier ();
   if (DEBUG_MANGLE)
@@ -4405,6 +4373,37 @@ mangle_decomp (const tree decl, vec<tree
              IDENTIFIER_POINTER (id));
 
   input_location = saved_loc;
+
+  if (warn_abi && G.need_abi_warning)
+    {
+      const char fabi_version[] = "-fabi-version";
+      tree id2 = id;
+      int save_ver = flag_abi_version;
+
+      if (flag_abi_version != warn_abi_version)
+	{
+	  flag_abi_version = warn_abi_version;
+	  id2 = mangle_decomp (decl, decls);
+	  flag_abi_version = save_ver;
+	}
+
+      if (id2 == id)
+	/* OK.  */;
+      else if (warn_abi_version != 0
+	       && abi_version_at_least (warn_abi_version))
+	warning_at (DECL_SOURCE_LOCATION (G.entity), OPT_Wabi,
+		    "the mangled name of %qD changed between "
+		    "%<%s=%d%> (%qD) and %<%s=%d%> (%qD)",
+		    G.entity, fabi_version, warn_abi_version, id2,
+		    fabi_version, save_ver, id);
+      else
+	warning_at (DECL_SOURCE_LOCATION (G.entity), OPT_Wabi,
+		    "the mangled name of %qD changes between "
+		    "%<%s=%d%> (%qD) and %<%s=%d%> (%qD)",
+		    G.entity, fabi_version, save_ver, id,
+		    fabi_version, warn_abi_version, id2);
+    }
+
   return id;
 }
 
@@ -4574,6 +4573,13 @@ write_guarded_var_name (const tree varia
     /* The name of a guard variable for a reference temporary should refer
        to the reference, not the temporary.  */
     write_string (IDENTIFIER_POINTER (DECL_NAME (variable)) + 4);
+  else if (DECL_DECOMPOSITION_P (variable)
+	   && DECL_NAME (variable) == NULL_TREE
+	   && startswith (IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (variable)),
+			  "_Z"))
+    /* The name of a guard variable for a structured binding needs special
+       casing.  */
+    write_string (IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (variable)) + 2);
   else
     write_name (variable, /*ignore_local_scope=*/0);
 }
@@ -4640,7 +4646,7 @@ mangle_ref_init_variable (const tree var
   start_mangling (variable);
   write_string ("_ZGR");
   check_abi_tags (variable);
-  write_name (variable, /*ignore_local_scope=*/0);
+  write_guarded_var_name (variable);
   /* Avoid name clashes with aggregate initialization of multiple
      references at once.  */
   write_compact_number (current_ref_temp_count++);
--- gcc/cp/parser.cc.jj	2023-08-31 14:31:35.963352093 +0200
+++ gcc/cp/parser.cc	2023-08-31 20:48:21.127722180 +0200
@@ -2393,7 +2393,7 @@ static tree cp_parser_c_for
 static tree cp_parser_range_for
   (cp_parser *, tree, tree, tree, bool, unsigned short, bool, bool);
 static void do_range_for_auto_deduction
-  (tree, tree, tree, unsigned int);
+  (tree, tree, cp_decomp *);
 static tree cp_parser_perform_range_for_lookup
   (tree, tree *, tree *);
 static tree cp_parser_range_for_member_function
@@ -13854,8 +13854,7 @@ cp_parser_range_for (cp_parser *parser,
   tree stmt, range_expr;
   auto_vec <cxx_binding *, 16> bindings;
   auto_vec <tree, 16> names;
-  tree decomp_first_name = NULL_TREE;
-  unsigned int decomp_cnt = 0;
+  cp_decomp decomp_d, *decomp = NULL;
 
   /* Get the range declaration momentarily out of the way so that
      the range expression doesn't clash with it. */
@@ -13872,9 +13871,11 @@ cp_parser_range_for (cp_parser *parser,
 	    {
 	      tree d = range_decl;
 	      range_decl = TREE_OPERAND (v, 0);
-	      decomp_cnt = tree_to_uhwi (TREE_OPERAND (v, 1)) + 1;
-	      decomp_first_name = d;
-	      for (unsigned int i = 0; i < decomp_cnt; i++, d = DECL_CHAIN (d))
+	      decomp = &decomp_d;
+	      decomp->count = tree_to_uhwi (TREE_OPERAND (v, 1)) + 1;
+	      decomp->decl = d;
+	      for (unsigned int i = 0; i < decomp->count;
+		   i++, d = DECL_CHAIN (d))
 		{
 		  tree name = DECL_NAME (d);
 		  names.safe_push (name);
@@ -13928,15 +13929,13 @@ cp_parser_range_for (cp_parser *parser,
       if (!type_dependent_expression_p (range_expr)
 	  /* do_auto_deduction doesn't mess with template init-lists.  */
 	  && !BRACE_ENCLOSED_INITIALIZER_P (range_expr))
-	do_range_for_auto_deduction (range_decl, range_expr, decomp_first_name,
-				     decomp_cnt);
+	do_range_for_auto_deduction (range_decl, range_expr, decomp);
     }
   else
     {
       stmt = begin_for_stmt (scope, init);
-      stmt = cp_convert_range_for (stmt, range_decl, range_expr,
-				   decomp_first_name, decomp_cnt, ivdep,
-				   unroll, novector);
+      stmt = cp_convert_range_for (stmt, range_decl, range_expr, decomp,
+				   ivdep, unroll, novector);
     }
   return stmt;
 }
@@ -13968,8 +13967,7 @@ build_range_temp (tree range_expr)
    a shortcut version of cp_convert_range_for.  */
 
 static void
-do_range_for_auto_deduction (tree decl, tree range_expr,
-			     tree decomp_first_name, unsigned int decomp_cnt)
+do_range_for_auto_deduction (tree decl, tree range_expr, cp_decomp *decomp)
 {
   tree auto_node = type_uses_auto (TREE_TYPE (decl));
   if (auto_node)
@@ -13990,7 +13988,7 @@ do_range_for_auto_deduction (tree decl,
 						tf_warning_or_error,
 						adc_variable_type);
 	  if (VAR_P (decl) && DECL_DECOMPOSITION_P (decl))
-	    cp_finish_decomp (decl, decomp_first_name, decomp_cnt);
+	    cp_finish_decomp (decl, decomp);
 	}
     }
 }
@@ -14113,8 +14111,8 @@ warn_for_range_copy (tree decl, tree exp
 
 tree
 cp_convert_range_for (tree statement, tree range_decl, tree range_expr,
-		      tree decomp_first_name, unsigned int decomp_cnt,
-		      bool ivdep, unsigned short unroll, bool novector)
+		      cp_decomp *decomp, bool ivdep, unsigned short unroll,
+		      bool novector)
 {
   tree begin, end;
   tree iter_type, begin_expr, end_expr;
@@ -14182,17 +14180,14 @@ cp_convert_range_for (tree statement, tr
 				     tf_warning_or_error);
   finish_for_expr (expression, statement);
 
-  if (VAR_P (range_decl) && DECL_DECOMPOSITION_P (range_decl))
-    cp_maybe_mangle_decomp (range_decl, decomp_first_name, decomp_cnt);
-
   /* The declaration is initialized with *__begin inside the loop body.  */
   tree deref_begin = build_x_indirect_ref (input_location, begin, RO_UNARY_STAR,
 					   NULL_TREE, tf_warning_or_error);
   cp_finish_decl (range_decl, deref_begin,
 		  /*is_constant_init*/false, NULL_TREE,
-		  LOOKUP_ONLYCONVERTING);
+		  LOOKUP_ONLYCONVERTING, decomp);
   if (VAR_P (range_decl) && DECL_DECOMPOSITION_P (range_decl))
-    cp_finish_decomp (range_decl, decomp_first_name, decomp_cnt);
+    cp_finish_decomp (range_decl, decomp);
 
   warn_for_range_copy (range_decl, deref_begin);
 
@@ -15890,18 +15885,20 @@ cp_parser_decomposition_declaration (cp_
 
       if (decl != error_mark_node)
 	{
-	  cp_maybe_mangle_decomp (decl, prev, v.length ());
+	  cp_decomp decomp = { prev, v.length () };
 	  cp_finish_decl (decl, initializer, non_constant_p, NULL_TREE,
-			  (is_direct_init ? LOOKUP_NORMAL : LOOKUP_IMPLICIT));
-	  cp_finish_decomp (decl, prev, v.length ());
+			  (is_direct_init ? LOOKUP_NORMAL : LOOKUP_IMPLICIT),
+			  &decomp);
+	  cp_finish_decomp (decl, &decomp);
 	}
     }
   else if (decl != error_mark_node)
     {
       *maybe_range_for_decl = prev;
+      cp_decomp decomp = { prev, v.length () };
       /* Ensure DECL_VALUE_EXPR is created for all the decls but
 	 the underlying DECL.  */
-      cp_finish_decomp (decl, prev, v.length ());
+      cp_finish_decomp (decl, &decomp);
     }
 
   if (pushed_scope)
@@ -43521,8 +43518,7 @@ cp_convert_omp_range_for (tree &this_pre
 	  && !BRACE_ENCLOSED_INITIALIZER_P (init))
 	{
 	  tree d = decl;
-	  tree decomp_first_name = NULL_TREE;
-	  unsigned decomp_cnt = 0;
+	  cp_decomp decomp_d, *decomp = NULL;
 	  if (decl != error_mark_node && DECL_HAS_VALUE_EXPR_P (decl))
 	    {
 	      tree v = DECL_VALUE_EXPR (decl);
@@ -43531,11 +43527,12 @@ cp_convert_omp_range_for (tree &this_pre
 		  && DECL_DECOMPOSITION_P (TREE_OPERAND (v, 0)))
 		{
 		  d = TREE_OPERAND (v, 0);
-		  decomp_cnt = tree_to_uhwi (TREE_OPERAND (v, 1)) + 1;
-		  decomp_first_name = decl;
+		  decomp = &decomp_d;
+		  decomp->count = tree_to_uhwi (TREE_OPERAND (v, 1)) + 1;
+		  decomp->decl = decl;
 		}
 	    }
-	  do_range_for_auto_deduction (d, init, decomp_first_name, decomp_cnt);
+	  do_range_for_auto_deduction (d, init, decomp);
 	}
       cond = global_namespace;
       incr = NULL_TREE;
@@ -43626,8 +43623,7 @@ cp_convert_omp_range_for (tree &this_pre
   decl = begin;
   /* Defer popping sl here.  */
 
-  tree decomp_first_name = NULL_TREE;
-  unsigned decomp_cnt = 0;
+  cp_decomp decomp_d, *decomp = NULL;
   if (orig_decl != error_mark_node && DECL_HAS_VALUE_EXPR_P (orig_decl))
     {
       tree v = DECL_VALUE_EXPR (orig_decl);
@@ -43637,8 +43633,9 @@ cp_convert_omp_range_for (tree &this_pre
 	{
 	  tree d = orig_decl;
 	  orig_decl = TREE_OPERAND (v, 0);
-	  decomp_cnt = tree_to_uhwi (TREE_OPERAND (v, 1)) + 1;
-	  decomp_first_name = d;
+	  decomp = &decomp_d;
+	  decomp->count = tree_to_uhwi (TREE_OPERAND (v, 1)) + 1;
+	  decomp->decl = d;
 	}
     }
 
@@ -43651,10 +43648,10 @@ cp_convert_omp_range_for (tree &this_pre
 	{
 	  TREE_TYPE (orig_decl) = do_auto_deduction (TREE_TYPE (orig_decl),
 						     t, auto_node);
-	  if (decomp_first_name)
+	  if (decomp)
 	    {
 	      ++processing_template_decl;
-	      cp_finish_decomp (orig_decl, decomp_first_name, decomp_cnt);
+	      cp_finish_decomp (orig_decl, decomp);
 	      --processing_template_decl;
 	      if (!processing_template_decl)
 		clear_has_value_expr = true;
@@ -43670,8 +43667,9 @@ cp_convert_omp_range_for (tree &this_pre
      the whole loop nest.  The remaining elements are decls of derived
      decomposition variables that are bound inside the loop body.  This
      structure is further mangled by finish_omp_for into the form required
-     for the OMP_FOR_ORIG_DECLS field of the OMP_FOR tree node.  */
-  tree v = make_tree_vec (decomp_cnt + 3);
+     for the OMP_FOR_ORIG_DECLS field of the OMP_FOR tree node.  */\
+  unsigned decomp_cnt = decomp ? decomp->count : 0;
+  tree v = make_tree_vec (decomp_cnt);
   TREE_VEC_ELT (v, 0) = range_temp_decl;
   TREE_VEC_ELT (v, 1) = end;
   TREE_VEC_ELT (v, 2) = orig_decl;
@@ -43686,13 +43684,13 @@ cp_convert_omp_range_for (tree &this_pre
 	     name but the DECL_VALUE_EXPR will be dependent.  Hide those
 	     from folding of other loop initializers e.g. for warning
 	     purposes until cp_finish_omp_range_for.  */
-	  gcc_checking_assert (DECL_HAS_VALUE_EXPR_P (decomp_first_name)
-			       || (TREE_TYPE (decomp_first_name)
+	  gcc_checking_assert (DECL_HAS_VALUE_EXPR_P (decomp->decl)
+			       || (TREE_TYPE (decomp->decl)
 				   == error_mark_node));
-	  DECL_HAS_VALUE_EXPR_P (decomp_first_name) = 0;
+	  DECL_HAS_VALUE_EXPR_P (decomp->decl) = 0;
 	}
-      TREE_VEC_ELT (v, i + 3) = decomp_first_name;
-      decomp_first_name = DECL_CHAIN (decomp_first_name);
+      TREE_VEC_ELT (v, i + 3) = decomp->decl;
+      decomp->decl = DECL_CHAIN (decomp->decl);
     }
   orig_decl = tree_cons (NULL_TREE, NULL_TREE, v);
 }
@@ -43706,27 +43704,26 @@ cp_finish_omp_range_for (tree orig, tree
   gcc_assert (TREE_CODE (orig) == TREE_LIST
 	      && TREE_CODE (TREE_CHAIN (orig)) == TREE_VEC);
   tree decl = TREE_VEC_ELT (TREE_CHAIN (orig), 2);
-  tree decomp_first_name = NULL_TREE;
-  unsigned int decomp_cnt = 0;
+  cp_decomp decomp_d, *decomp = NULL;
 
   if (VAR_P (decl) && DECL_DECOMPOSITION_P (decl))
     {
-      decomp_first_name = TREE_VEC_ELT (TREE_CHAIN (orig), 3);
-      decomp_cnt = TREE_VEC_LENGTH (TREE_CHAIN (orig)) - 3;
+      decomp = &decomp_d;
+      decomp_d.decl = TREE_VEC_ELT (TREE_CHAIN (orig), 3);
+      decomp_d.count = TREE_VEC_LENGTH (TREE_CHAIN (orig)) - 3;
       if (TREE_PUBLIC (TREE_CHAIN (orig)))
 	{
 	  /* Undo temporary clearing of DECL_HAS_VALUE_EXPR_P done
 	     by cp_convert_omp_range_for above.  */
 	  TREE_PUBLIC (TREE_CHAIN (orig)) = 0;
-	  tree d = decomp_first_name;
-	  for (unsigned i = 0; i < decomp_cnt; i++)
+	  tree d = decomp_d.decl;
+	  for (unsigned i = 0; i < decomp_d.count; i++)
 	    {
 	      if (TREE_TYPE (d) != error_mark_node)
 		DECL_HAS_VALUE_EXPR_P (d) = 1;
 	      d = DECL_CHAIN (d);
 	    }
 	}
-      cp_maybe_mangle_decomp (decl, decomp_first_name, decomp_cnt);
     }
 
   /* The declaration is initialized with *__begin inside the loop body.  */
@@ -43734,9 +43731,9 @@ cp_finish_omp_range_for (tree orig, tree
 		  build_x_indirect_ref (input_location, begin, RO_UNARY_STAR,
 					NULL_TREE, tf_warning_or_error),
 		  /*is_constant_init*/false, NULL_TREE,
-		  LOOKUP_ONLYCONVERTING);
+		  LOOKUP_ONLYCONVERTING, decomp);
   if (VAR_P (decl) && DECL_DECOMPOSITION_P (decl))
-    cp_finish_decomp (decl, decomp_first_name, decomp_cnt);
+    cp_finish_decomp (decl, decomp);
 }
 
 /* Return true if next tokens contain a standard attribute that contains
--- gcc/cp/pt.cc.jj	2023-08-28 13:55:55.868367710 +0200
+++ gcc/cp/pt.cc	2023-08-31 20:48:21.127722180 +0200
@@ -18352,7 +18352,7 @@ tsubst_copy_asm_operands (tree t, tree a
 static tree *omp_parallel_combined_clauses;
 
 static tree tsubst_decomp_names (tree, tree, tree, tsubst_flags_t, tree,
-				 tree *, unsigned int *);
+				 cp_decomp *);
 
 /* Substitute one OMP_FOR iterator.  */
 
@@ -18383,28 +18383,27 @@ tsubst_omp_for_iterator (tree t, int i,
 	      && VAR_P (TREE_OPERAND (v, 0))
 	      && DECL_DECOMPOSITION_P (TREE_OPERAND (v, 0)))
 	    {
-	      tree decomp_first = NULL_TREE;
-	      unsigned decomp_cnt = 0;
+	      cp_decomp decomp_d = { NULL_TREE, 0 };
 	      tree d = tsubst_decl (TREE_OPERAND (v, 0), args, complain);
 	      maybe_push_decl (d);
 	      d = tsubst_decomp_names (d, TREE_OPERAND (v, 0), args, complain,
-				       in_decl, &decomp_first, &decomp_cnt);
+				       in_decl, &decomp_d);
 	      decomp = true;
 	      if (d == error_mark_node)
 		decl = error_mark_node;
 	      else
-		for (unsigned int i = 0; i < decomp_cnt; i++)
+		for (unsigned int i = 0; i < decomp_d.count; i++)
 		  {
-		    if (!DECL_HAS_VALUE_EXPR_P (decomp_first))
+		    if (!DECL_HAS_VALUE_EXPR_P (decomp_d.decl))
 		      {
 			tree v = build_nt (ARRAY_REF, d,
-					   size_int (decomp_cnt - i - 1),
+					   size_int (decomp_d.count - i - 1),
 					   NULL_TREE, NULL_TREE);
-			SET_DECL_VALUE_EXPR (decomp_first, v);
-			DECL_HAS_VALUE_EXPR_P (decomp_first) = 1;
+			SET_DECL_VALUE_EXPR (decomp_d.decl, v);
+			DECL_HAS_VALUE_EXPR_P (decomp_d.decl) = 1;
 		      }
-		    fit_decomposition_lang_decl (decomp_first, d);
-		    decomp_first = DECL_CHAIN (decomp_first);
+		    fit_decomposition_lang_decl (decomp_d.decl, d);
+		    decomp_d.decl = DECL_CHAIN (decomp_d.decl);
 		  }
 	    }
 	}
@@ -18723,11 +18722,10 @@ tsubst_find_omp_teams (tree *tp, int *wa
 
 static tree
 tsubst_decomp_names (tree decl, tree pattern_decl, tree args,
-		     tsubst_flags_t complain, tree in_decl, tree *first,
-		     unsigned int *cnt)
+		     tsubst_flags_t complain, tree in_decl, cp_decomp *decomp)
 {
   tree decl2, decl3, prev = decl;
-  *cnt = 0;
+  decomp->count = 0;
   gcc_assert (DECL_NAME (decl) == NULL_TREE);
   for (decl2 = DECL_CHAIN (pattern_decl);
        decl2
@@ -18736,12 +18734,12 @@ tsubst_decomp_names (tree decl, tree pat
        && DECL_NAME (decl2);
        decl2 = DECL_CHAIN (decl2))
     {
-      if (TREE_TYPE (decl2) == error_mark_node && *cnt == 0)
+      if (TREE_TYPE (decl2) == error_mark_node && decomp->count == 0)
 	{
 	  gcc_assert (errorcount);
 	  return error_mark_node;
 	}
-      (*cnt)++;
+      decomp->count++;
       gcc_assert (DECL_DECOMP_BASE (decl2) == pattern_decl);
       gcc_assert (DECL_HAS_VALUE_EXPR_P (decl2));
       tree v = DECL_VALUE_EXPR (decl2);
@@ -18771,7 +18769,7 @@ tsubst_decomp_names (tree decl, tree pat
       else
 	prev = decl3;
     }
-  *first = prev;
+  decomp->decl = prev;
   return decl;
 }
 
@@ -19043,8 +19041,8 @@ tsubst_expr (tree t, tree args, tsubst_f
 		else
 		  {
 		    bool const_init = false;
-		    unsigned int cnt = 0;
-		    tree first = NULL_TREE, ndecl = error_mark_node;
+		    cp_decomp decomp_d, *decomp = NULL;
+		    tree ndecl = error_mark_node;
 		    tree asmspec_tree = NULL_TREE;
 		    maybe_push_decl (decl);
 
@@ -19056,9 +19054,11 @@ tsubst_expr (tree t, tree args, tsubst_f
 		    if (VAR_P (decl)
 			&& DECL_DECOMPOSITION_P (decl)
 			&& TREE_TYPE (pattern_decl) != error_mark_node)
-		      ndecl = tsubst_decomp_names (decl, pattern_decl, args,
-						   complain, in_decl, &first,
-						   &cnt);
+		      {
+			decomp = &decomp_d;
+			ndecl = tsubst_decomp_names (decl, pattern_decl, args,
+						     complain, in_decl, decomp);
+		      }
 
 		    init = tsubst_init (init, decl, args, complain, in_decl);
 
@@ -19066,9 +19066,6 @@ tsubst_expr (tree t, tree args, tsubst_f
 		      const_init = (DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P
 				    (pattern_decl));
 
-		    if (ndecl != error_mark_node)
-		      cp_maybe_mangle_decomp (ndecl, first, cnt);
-
 		    /* In a non-template function, VLA type declarations are
 		       handled in grokdeclarator; for templates, handle them
 		       now.  */
@@ -19085,10 +19082,11 @@ tsubst_expr (tree t, tree args, tsubst_f
 			TREE_TYPE (asmspec_tree) = char_array_type_node;
 		      }
 
-		    cp_finish_decl (decl, init, const_init, asmspec_tree, 0);
+		    cp_finish_decl (decl, init, const_init, asmspec_tree, 0,
+				    decomp);
 
 		    if (ndecl != error_mark_node)
-		      cp_finish_decomp (ndecl, first, cnt);
+		      cp_finish_decomp (ndecl, decomp);
 		  }
 	      }
 	  }
@@ -19127,12 +19125,13 @@ tsubst_expr (tree t, tree args, tsubst_f
         maybe_push_decl (decl);
         expr = RECUR (RANGE_FOR_EXPR (t));
 
-	tree decomp_first = NULL_TREE;
-	unsigned decomp_cnt = 0;
+	cp_decomp decomp_d, *decomp = NULL;
 	if (VAR_P (decl) && DECL_DECOMPOSITION_P (decl))
-	  decl = tsubst_decomp_names (decl, RANGE_FOR_DECL (t), args,
-				      complain, in_decl,
-				      &decomp_first, &decomp_cnt);
+	  {
+	    decomp = &decomp_d;
+	    decl = tsubst_decomp_names (decl, RANGE_FOR_DECL (t), args,
+					complain, in_decl, decomp);
+	  }
 
 	if (processing_template_decl)
 	  {
@@ -19140,15 +19139,14 @@ tsubst_expr (tree t, tree args, tsubst_f
 	    RANGE_FOR_UNROLL (stmt) = RANGE_FOR_UNROLL (t);
 	    RANGE_FOR_NOVECTOR (stmt) = RANGE_FOR_NOVECTOR (t);
 	    finish_range_for_decl (stmt, decl, expr);
-	    if (decomp_first && decl != error_mark_node)
-	      cp_finish_decomp (decl, decomp_first, decomp_cnt);
+	    if (decomp && decl != error_mark_node)
+	      cp_finish_decomp (decl, decomp);
 	  }
 	else
 	  {
 	    unsigned short unroll = (RANGE_FOR_UNROLL (t)
 				     ? tree_to_uhwi (RANGE_FOR_UNROLL (t)) : 0);
-	    stmt = cp_convert_range_for (stmt, decl, expr,
-					 decomp_first, decomp_cnt,
+	    stmt = cp_convert_range_for (stmt, decl, expr, decomp,
 					 RANGE_FOR_IVDEP (t), unroll,
 					 RANGE_FOR_NOVECTOR (t));
 	  }
--- gcc/testsuite/g++.dg/cpp2a/decomp8.C.jj	2023-08-31 19:53:31.205280379 +0200
+++ gcc/testsuite/g++.dg/cpp2a/decomp8.C	2023-08-31 19:53:31.205280379 +0200
@@ -0,0 +1,74 @@
+// PR c++/111069
+// { dg-do compile { target c++11 } }
+// { dg-options "" }
+
+extern int a[2];
+struct Y { int b, c, d; };
+
+inline int
+freddy ()
+{
+  static auto [i, j] = a;		// { dg-warning "structured bindings only available with" "" { target c++14_down } }
+					// { dg-warning "structured binding declaration can be 'static' only in" "" { target c++17_down } .-1 }
+  static auto [k, l] = a;		// { dg-warning "structured bindings only available with" "" { target c++14_down } }
+					// { dg-warning "structured binding declaration can be 'static' only in" "" { target c++17_down } .-1 }
+  int ret = ++i + ++k;
+  {
+    static auto [i, j] = a;		// { dg-warning "structured bindings only available with" "" { target c++14_down } }
+					// { dg-warning "structured binding declaration can be 'static' only in" "" { target c++17_down } .-1 }
+    static auto [k, l] = a;		// { dg-warning "structured bindings only available with" "" { target c++14_down } }
+					// { dg-warning "structured binding declaration can be 'static' only in" "" { target c++17_down } .-1 }
+    ret += ++i + ++k;
+  }
+  {
+    static auto [i, j] = a;		// { dg-warning "structured bindings only available with" "" { target c++14_down } }
+					// { dg-warning "structured binding declaration can be 'static' only in" "" { target c++17_down } .-1 }
+    static auto [k, l] = a;		// { dg-warning "structured bindings only available with" "" { target c++14_down } }
+					// { dg-warning "structured binding declaration can be 'static' only in" "" { target c++17_down } .-1 }
+    ret += ++i + ++k;
+  }
+  return ret;
+}
+
+namespace N
+{
+  namespace M
+  {
+    template <int N>
+    inline int
+    corge ()
+    {
+      static auto [i, j] = a;		// { dg-warning "structured bindings only available with" "" { target c++14_down } }
+					// { dg-warning "structured binding declaration can be 'static' only in" "" { target c++17_down } .-1 }
+      static auto && [u, v, w] = Y{};	// { dg-warning "structured bindings only available with" "" { target c++14_down } }
+					// { dg-warning "structured binding declaration can be 'static' only in" "" { target c++17_down } .-1 }
+      int ret = ++i + ++u;
+      {
+	static auto && [u, v, w] = Y{};	// { dg-warning "structured bindings only available with" "" { target c++14_down } }
+					// { dg-warning "structured binding declaration can be 'static' only in" "" { target c++17_down } .-1 }
+	ret += ++v;
+      }
+      return ret;
+    }
+  }
+}
+
+int (*p) () = &freddy;
+int (*q) () = N::M::corge<3>;
+
+// { dg-final { scan-assembler "_ZZ6freddyvEDC1i1jE" } }
+// { dg-final { scan-assembler "_ZZ6freddyvEDC1i1jE_0" } }
+// { dg-final { scan-assembler "_ZZ6freddyvEDC1i1jE_1" } }
+// { dg-final { scan-assembler "_ZZ6freddyvEDC1k1lE" } }
+// { dg-final { scan-assembler "_ZZ6freddyvEDC1k1lE_0" } }
+// { dg-final { scan-assembler "_ZZ6freddyvEDC1k1lE_1" } }
+// { dg-final { scan-assembler "_ZZN1N1M5corgeILi3EEEivEDC1i1jE" } }
+// { dg-final { scan-assembler "_ZGVZ6freddyvEDC1i1jE" } }
+// { dg-final { scan-assembler "_ZGVZ6freddyvEDC1i1jE_0" } }
+// { dg-final { scan-assembler "_ZGVZ6freddyvEDC1i1jE_1" } }
+// { dg-final { scan-assembler "_ZGVZ6freddyvEDC1k1lE" } }
+// { dg-final { scan-assembler "_ZGVZ6freddyvEDC1k1lE_0" } }
+// { dg-final { scan-assembler "_ZGVZ6freddyvEDC1k1lE_1" } }
+// { dg-final { scan-assembler "_ZGVZN1N1M5corgeILi3EEEivEDC1i1jE" } }
+// { dg-final { scan-assembler "_ZGRZN1N1M5corgeILi3EEEivEDC1u1v1wE_" } }
+// { dg-final { scan-assembler "_ZGRZN1N1M5corgeILi3EEEivEDC1u1v1wE_0_" } }
--- gcc/testsuite/g++.dg/cpp2a/decomp9.C.jj	2023-08-31 19:53:31.205280379 +0200
+++ gcc/testsuite/g++.dg/cpp2a/decomp9.C	2023-08-31 19:53:31.205280379 +0200
@@ -0,0 +1,82 @@
+// PR c++/111069
+// { dg-do compile { target c++11 } }
+// { dg-options "" }
+
+struct [[gnu::abi_tag ("foobar")]] S { int i; };
+extern S a[2];
+struct [[gnu::abi_tag ("qux")]] T { int i; S j; int k; };
+extern T b[2];
+
+namespace N {
+  auto [i, j] = a;		// { dg-warning "structured bindings only available with" "" { target c++14_down } }
+  auto [k, l] = b;		// { dg-warning "structured bindings only available with" "" { target c++14_down } }
+}
+
+inline int
+foo ()
+{
+  static auto [m, n] = a;	// { dg-warning "structured bindings only available with" "" { target c++14_down } }
+				// { dg-warning "structured binding declaration can be 'static' only in" "" { target c++17_down } .-1 }
+  static auto [o, p] = b;	// { dg-warning "structured bindings only available with" "" { target c++14_down } }
+				// { dg-warning "structured binding declaration can be 'static' only in" "" { target c++17_down } .-1 }
+  int ret = ++N::i.i + ++N::k.i;
+  {
+    static auto [m, n] = a;	// { dg-warning "structured bindings only available with" "" { target c++14_down } }
+				// { dg-warning "structured binding declaration can be 'static' only in" "" { target c++17_down } .-1 }
+
+    ret += ++n.i;
+  }
+  {
+    static auto [m, n] = a;	// { dg-warning "structured bindings only available with" "" { target c++14_down } }
+				// { dg-warning "structured binding declaration can be 'static' only in" "" { target c++17_down } .-1 }
+
+    ret += ++n.i;
+  }
+  ret += ++m.i + ++o.i;
+  return ret;
+}
+
+template <typename T>
+inline int
+bar ()
+{
+  static auto [m, n] = a;	// { dg-warning "structured bindings only available with" "" { target c++14_down } }
+				// { dg-warning "structured binding declaration can be 'static' only in" "" { target c++17_down } .-1 }
+  static auto [o, p] = b;	// { dg-warning "structured bindings only available with" "" { target c++14_down } }
+				// { dg-warning "structured binding declaration can be 'static' only in" "" { target c++17_down } .-1 }
+  int ret = 0;
+  {
+    static auto [m, n] = a;	// { dg-warning "structured bindings only available with" "" { target c++14_down } }
+				// { dg-warning "structured binding declaration can be 'static' only in" "" { target c++17_down } .-1 }
+    ret += ++n.i;
+  }
+  {
+    static auto [m, n] = a;	// { dg-warning "structured bindings only available with" "" { target c++14_down } }
+				// { dg-warning "structured binding declaration can be 'static' only in" "" { target c++17_down } .-1 }
+    ret += ++n.i;
+  }
+  ret += ++m.i + ++o.i;
+  return ret;
+}
+
+int (*p) () = &foo;
+int (*q) () = &bar<T>;
+
+// { dg-final { scan-assembler "_ZZ3foovEDC1m1nEB6foobar" } }
+// { dg-final { scan-assembler "_ZZ3foovEDC1m1nEB6foobar_0" } }
+// { dg-final { scan-assembler "_ZZ3foovEDC1m1nEB6foobar_1" } }
+// { dg-final { scan-assembler "_ZZ3foovEDC1o1pEB3qux" } }
+// { dg-final { scan-assembler "_ZZ3barI1TB3quxEivEDC1m1nEB6foobar" } }
+// { dg-final { scan-assembler "_ZZ3barI1TB3quxEivEDC1m1nEB6foobar_0" } }
+// { dg-final { scan-assembler "_ZZ3barI1TB3quxEivEDC1m1nEB6foobar_1" } }
+// { dg-final { scan-assembler "_ZZ3barI1TB3quxEivEDC1o1pEB3qux" } }
+// { dg-final { scan-assembler "_ZN1NDC1i1jEB6foobarE" } }
+// { dg-final { scan-assembler "_ZN1NDC1k1lEB3quxE" } }
+// { dg-final { scan-assembler "_ZGVZ3foovEDC1m1nEB6foobar" } }
+// { dg-final { scan-assembler "_ZGVZ3foovEDC1m1nEB6foobar_0" } }
+// { dg-final { scan-assembler "_ZGVZ3foovEDC1m1nEB6foobar_1" } }
+// { dg-final { scan-assembler "_ZGVZ3foovEDC1o1pEB3qux" } }
+// { dg-final { scan-assembler "_ZGVZ3barI1TB3quxEivEDC1m1nEB6foobar" } }
+// { dg-final { scan-assembler "_ZGVZ3barI1TB3quxEivEDC1m1nEB6foobar_0" } }
+// { dg-final { scan-assembler "_ZGVZ3barI1TB3quxEivEDC1m1nEB6foobar_1" } }
+// { dg-final { scan-assembler "_ZGVZ3barI1TB3quxEivEDC1o1pEB3qux" } }
--- gcc/testsuite/g++.dg/abi/macro0.C.jj	2022-10-11 10:00:07.456124822 +0200
+++ gcc/testsuite/g++.dg/abi/macro0.C	2023-08-31 19:53:31.222280146 +0200
@@ -1,6 +1,6 @@
 // This testcase will need to be kept in sync with c_common_post_options.
 // { dg-options "-fabi-version=0" }
 
-#if __GXX_ABI_VERSION != 1018
+#if __GXX_ABI_VERSION != 1019
 #error "Incorrect value of __GXX_ABI_VERSION"
 #endif


	Jakub


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

* Re: [PATCH] c++, v3: Fix up mangling of function/block scope static structured bindings and emit abi tags [PR111069]
  2023-08-31 19:14         ` [PATCH] c++, v3: " Jakub Jelinek
@ 2023-08-31 19:41           ` Jason Merrill
  0 siblings, 0 replies; 7+ messages in thread
From: Jason Merrill @ 2023-08-31 19:41 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: gcc-patches

On 8/31/23 15:14, Jakub Jelinek wrote:
> On Thu, Aug 31, 2023 at 01:11:57PM -0400, Jason Merrill wrote:
>>> 2023-08-28  Jakub Jelinek  <jakub@redhat.com>
>>>
>>> 	PR c++/111069
>>> gcc/
>>> 	* common.opt (fabi-version=): Document version 19.
>>> 	* doc/invoke.texi (-fabi-version=): Likewise.
>>> gcc/c-family/
>>> 	* c-opts.cc (c_common_post_options): Change latest_abi_version to 19.
>>> gcc/cp/
>>> 	* cp-tree.h (determine_local_discriminator): Add NAME argument with
>>> 	NULL_TREE default.
>>> 	(struct cp_decomp): New type.
>>
>> Maybe cp_finish_decomp should take this as well?  And tsubst_decomp_names,
>> and various other functions with decomp_first_name/decomp_cnt parms?
> 
> Ok, done below.
> 
>>> +  if (tree tags = get_abi_tags (decl))
>>> +    {
>>> +      /* We didn't emit ABI tags for structured bindings before ABI 19.  */
>>> +      if (!G.need_abi_warning
>>> +          && abi_warn_or_compat_version_crosses (19))
>>> +	G.need_abi_warning = 1;
>>
>> In general we should probably only warn about mangling changes if
>> TREE_PUBLIC (decl).
> 
> I have done that but I think it ought to be unnecessary, because
> check_abi_tags starts with
>    if (!TREE_PUBLIC (decl))
>      /* No need to worry about things local to this TU.  */
>      return NULL_TREE;
> 
> Here is an updated patch, so far just tested with
> make check-g++ GXX_TESTSUITE_STDS=98,11,14,17,20,2b,2c RUNTESTFLAGS="dg.exp='*decomp*'"
> ok for trunk if it passes full bootstrap/regtest?

OK.

> 2023-08-31  Jakub Jelinek  <jakub@redhat.com>
> 
> 	PR c++/111069
> gcc/
> 	* common.opt (fabi-version=): Document version 19.
> 	* doc/invoke.texi (-fabi-version=): Likewise.
> gcc/c-family/
> 	* c-opts.cc (c_common_post_options): Change latest_abi_version to 19.
> gcc/cp/
> 	* cp-tree.h (determine_local_discriminator): Add NAME argument with
> 	NULL_TREE default.
> 	(struct cp_decomp): New type.
> 	(cp_finish_decl): Add DECOMP argument defaulted to nullptr.
> 	(cp_maybe_mangle_decomp): Remove declaration.
> 	(cp_finish_decomp): Add cp_decomp * argument, remove tree and unsigned
> 	args.
> 	(cp_convert_range_for): Likewise.
> 	* decl.cc (determine_local_discriminator): Add NAME argument, use it
> 	if non-NULL, otherwise compute it the old way.
> 	(maybe_commonize_var): Don't return early for structured bindings.
> 	(cp_finish_decl): Add DECOMP argument, if non-NULL, call
> 	cp_maybe_mangle_decomp.
> 	(cp_maybe_mangle_decomp): Make it static with a forward declaration.
> 	Call determine_local_discriminator.  Replace FIRST and COUNT arguments
> 	with DECOMP argument.
> 	(cp_finish_decomp): Replace FIRST and COUNT arguments with DECOMP
> 	argument.
> 	* mangle.cc (find_decomp_unqualified_name): Remove.
> 	(write_unqualified_name): Don't call find_decomp_unqualified_name.
> 	(mangle_decomp): Handle mangling of static function/block scope
> 	structured bindings.  Don't call decl_mangling_context twice.  Call
> 	check_abi_tags, call write_abi_tags for abi version >= 19 and emit
> 	-Wabi warnings if needed.
> 	(write_guarded_var_name): Handle structured bindings.
> 	(mangle_ref_init_variable): Use write_guarded_var_name.
> 	* parser.cc (cp_parser_range_for): Adjust do_range_for_auto_deduction
> 	and cp_convert_range_for callers.
> 	(do_range_for_auto_deduction): Replace DECOMP_FIRST_NAME and
> 	DECOMP_CNT arguments with DECOMP.  Adjust cp_finish_decomp caller.
> 	(cp_convert_range_for): Replace DECOMP_FIRST_NAME and
> 	DECOMP_CNT arguments with DECOMP.  Don't call cp_maybe_mangle_decomp,
> 	adjust cp_finish_decl and cp_finish_decomp callers.
> 	(cp_parser_decomposition_declaration): Don't call
> 	cp_maybe_mangle_decomp, adjust cp_finish_decl and cp_finish_decomp
> 	callers.
> 	(cp_convert_omp_range_for): Adjust do_range_for_auto_deduction
> 	and cp_finish_decomp callers.
> 	(cp_finish_omp_range_for): Don't call cp_maybe_mangle_decomp,
> 	adjust cp_finish_decl and cp_finish_decomp callers.
> 	* pt.cc (tsubst_omp_for_iterator): Adjust tsubst_decomp_names
> 	caller.
> 	(tsubst_decomp_names): Replace FIRST and CNT arguments with DECOMP.
> 	(tsubst_expr): Don't call cp_maybe_mangle_decomp, adjust
> 	tsubst_decomp_names, cp_finish_decl, cp_finish_decomp and
> 	cp_convert_range_for callers.
> gcc/testsuite/
> 	* g++.dg/cpp2a/decomp8.C: New test.
> 	* g++.dg/cpp2a/decomp9.C: New test.
> 	* g++.dg/abi/macro0.C: Expect __GXX_ABI_VERSION 1019 rather than
> 	1018.
> 
> --- gcc/common.opt.jj	2023-08-28 13:55:55.670370386 +0200
> +++ gcc/common.opt	2023-08-31 19:53:31.186280641 +0200
> @@ -1010,6 +1010,9 @@ Driver Undocumented
>   ; 18: Corrects errors in mangling of lambdas with additional context.
>   ;     Default in G++ 13.
>   ;
> +; 19: Emits ABI tags if needed in structured binding mangled names.
> +;     Default in G++ 14.
> +;
>   ; Additional positive integers will be assigned as new versions of
>   ; the ABI become the default version of the ABI.
>   fabi-version=
> --- gcc/doc/invoke.texi.jj	2023-08-31 19:52:16.734307207 +0200
> +++ gcc/doc/invoke.texi	2023-08-31 19:53:31.191280572 +0200
> @@ -3017,6 +3017,9 @@ in C++14 and up.
>   Version 18, which first appeard in G++ 13, fixes manglings of lambdas
>   that have additional context.
>   
> +Version 19, which first appeard in G++ 14, fixes manglings of structured
> +bindings to include ABI tags.
> +
>   See also @option{-Wabi}.
>   
>   @opindex fabi-compat-version
> --- gcc/c-family/c-opts.cc.jj	2023-08-28 13:55:55.613371156 +0200
> +++ gcc/c-family/c-opts.cc	2023-08-31 19:53:31.192280558 +0200
> @@ -974,7 +974,7 @@ c_common_post_options (const char **pfil
>   
>     /* Change flag_abi_version to be the actual current ABI level, for the
>        benefit of c_cpp_builtins, and to make comparison simpler.  */
> -  const int latest_abi_version = 18;
> +  const int latest_abi_version = 19;
>     /* Generate compatibility aliases for ABI v13 (8.2) by default.  */
>     const int abi_compat_default = 13;
>   
> --- gcc/cp/cp-tree.h.jj	2023-08-31 14:31:05.991763184 +0200
> +++ gcc/cp/cp-tree.h	2023-08-31 20:39:34.052154029 +0200
> @@ -6859,7 +6859,7 @@ extern void pop_switch				(void);
>   extern void note_break_stmt			(void);
>   extern bool note_iteration_stmt_body_start	(void);
>   extern void note_iteration_stmt_body_end	(bool);
> -extern void determine_local_discriminator	(tree);
> +extern void determine_local_discriminator	(tree, tree = NULL_TREE);
>   extern bool member_like_constrained_friend_p	(tree);
>   extern bool fns_correspond			(tree, tree);
>   extern int decls_match				(tree, tree, bool = true);
> @@ -6892,10 +6892,10 @@ extern tree start_decl				(const cp_decl
>   extern void start_decl_1			(tree, bool);
>   extern bool check_array_initializer		(tree, tree, tree);
>   extern void omp_declare_variant_finalize	(tree, tree);
> -extern void cp_finish_decl			(tree, tree, bool, tree, int);
> +struct cp_decomp { tree decl; unsigned int count; };
> +extern void cp_finish_decl			(tree, tree, bool, tree, int, cp_decomp * = nullptr);
>   extern tree lookup_decomp_type			(tree);
> -extern void cp_maybe_mangle_decomp		(tree, tree, unsigned int);
> -extern void cp_finish_decomp			(tree, tree, unsigned int);
> +extern void cp_finish_decomp			(tree, cp_decomp *);
>   extern int cp_complete_array_type		(tree *, tree, bool);
>   extern int cp_complete_array_type_or_error	(tree *, tree, bool, tsubst_flags_t);
>   extern tree build_ptrmemfunc_type		(tree);
> @@ -7312,7 +7312,7 @@ extern tree clone_attrs				(tree);
>   extern bool maybe_clone_body			(tree);
>   
>   /* In parser.cc */
> -extern tree cp_convert_range_for (tree, tree, tree, tree, unsigned int, bool,
> +extern tree cp_convert_range_for (tree, tree, tree, cp_decomp *, bool,
>   				  unsigned short, bool);
>   extern void cp_convert_omp_range_for (tree &, tree &, tree &,
>   				      tree &, tree &, tree &, tree &, tree &);
> --- gcc/cp/decl.cc.jj	2023-08-30 18:48:48.831597950 +0200
> +++ gcc/cp/decl.cc	2023-08-31 20:48:21.127722180 +0200
> @@ -911,15 +911,16 @@ static GTY((deletable)) vec<tree, va_gc>
>      generally very few of these in any particular function.  */
>   
>   void
> -determine_local_discriminator (tree decl)
> +determine_local_discriminator (tree decl, tree name)
>   {
>     auto_cond_timevar tv (TV_NAME_LOOKUP);
>     retrofit_lang_decl (decl);
>     tree ctx = DECL_CONTEXT (decl);
> -  tree name = (TREE_CODE (decl) == TYPE_DECL
> -	       && TYPE_UNNAMED_P (TREE_TYPE (decl))
> -	       ? NULL_TREE : DECL_NAME (decl));
>     size_t nelts = vec_safe_length (local_entities);
> +  if (name == NULL_TREE)
> +    name = (TREE_CODE (decl) == TYPE_DECL
> +	    && TYPE_UNNAMED_P (TREE_TYPE (decl))
> +	    ? NULL_TREE : DECL_NAME (decl));
>     for (size_t i = 0; i < nelts; i += 2)
>       {
>         tree *pair = &(*local_entities)[i];
> @@ -6417,8 +6418,9 @@ layout_var_decl (tree decl)
>   void
>   maybe_commonize_var (tree decl)
>   {
> -  /* Don't mess with __FUNCTION__ and similar.  */
> -  if (DECL_ARTIFICIAL (decl))
> +  /* Don't mess with __FUNCTION__ and similar.  But do handle structured
> +     bindings.  */
> +  if (DECL_ARTIFICIAL (decl) && !DECL_DECOMPOSITION_P (decl))
>       return;
>   
>     /* Static data in a function with comdat linkage also has comdat
> @@ -8212,6 +8214,8 @@ omp_declare_variant_finalize (tree decl,
>       }
>   }
>   
> +static void cp_maybe_mangle_decomp (tree, cp_decomp *);
> +
>   /* Finish processing of a declaration;
>      install its line number and initial value.
>      If the length of an array type is not known before,
> @@ -8221,11 +8225,14 @@ omp_declare_variant_finalize (tree decl,
>      true, then INIT is an integral constant expression.
>   
>      FLAGS is LOOKUP_ONLYCONVERTING if the = init syntax was used, else 0
> -   if the (init) syntax was used.  */
> +   if the (init) syntax was used.
> +
> +   DECOMP is first identifier's DECL and identifier count in a structured
> +   bindings, nullptr if not a structured binding.  */
>   
>   void
>   cp_finish_decl (tree decl, tree init, bool init_const_expr_p,
> -		tree asmspec_tree, int flags)
> +		tree asmspec_tree, int flags, cp_decomp *decomp)
>   {
>     vec<tree, va_gc> *cleanups = NULL;
>     const char *asmspec = NULL;
> @@ -8600,6 +8607,9 @@ cp_finish_decl (tree decl, tree init, bo
>   	  return;
>   	}
>   
> +      if (decomp)
> +	cp_maybe_mangle_decomp (decl, decomp);
> +
>         /* If this is a local variable that will need a mangled name,
>   	 register it now.  We must do this before processing the
>   	 initializer for the variable, since the initialization might
> @@ -9070,18 +9080,37 @@ lookup_decomp_type (tree v)
>   /* Mangle a decomposition declaration if needed.  Arguments like
>      in cp_finish_decomp.  */
>   
> -void
> -cp_maybe_mangle_decomp (tree decl, tree first, unsigned int count)
> +static void
> +cp_maybe_mangle_decomp (tree decl, cp_decomp *decomp)
>   {
>     if (!processing_template_decl
>         && !error_operand_p (decl)
>         && TREE_STATIC (decl))
>       {
>         auto_vec<tree, 16> v;
> -      v.safe_grow (count, true);
> -      tree d = first;
> -      for (unsigned int i = 0; i < count; i++, d = DECL_CHAIN (d))
> -	v[count - i - 1] = d;
> +      v.safe_grow (decomp->count, true);
> +      tree d = decomp->decl;
> +      for (unsigned int i = 0; i < decomp->count; i++, d = DECL_CHAIN (d))
> +	v[decomp->count - i - 1] = d;
> +      if (DECL_FUNCTION_SCOPE_P (decl))
> +	{
> +	  size_t sz = 3;
> +	  for (unsigned int i = 0; i < decomp->count; ++i)
> +	    sz += IDENTIFIER_LENGTH (DECL_NAME (v[i])) + 1;
> +	  char *name = XALLOCAVEC (char, sz);
> +	  name[0] = 'D';
> +	  name[1] = 'C';
> +	  char *p = name + 2;
> +	  for (unsigned int i = 0; i < decomp->count; ++i)
> +	    {
> +	      size_t len = IDENTIFIER_LENGTH (DECL_NAME (v[i]));
> +	      *p++ = ' ';
> +	      memcpy (p, IDENTIFIER_POINTER (DECL_NAME (v[i])), len);
> +	      p += len;
> +	    }
> +	  *p = '\0';
> +	  determine_local_discriminator (decl, get_identifier (name));
> +	}
>         SET_DECL_ASSEMBLER_NAME (decl, mangle_decomp (decl, v));
>         maybe_apply_pragma_weak (decl);
>       }
> @@ -9093,8 +9122,10 @@ cp_maybe_mangle_decomp (tree decl, tree
>      those decls.  */
>   
>   void
> -cp_finish_decomp (tree decl, tree first, unsigned int count)
> +cp_finish_decomp (tree decl, cp_decomp *decomp)
>   {
> +  tree first = decomp->decl;
> +  unsigned count = decomp->count;
>     if (error_operand_p (decl))
>       {
>        error_out:
> --- gcc/cp/mangle.cc.jj	2023-08-28 13:55:55.819368372 +0200
> +++ gcc/cp/mangle.cc	2023-08-31 20:48:21.127722180 +0200
> @@ -1347,51 +1347,6 @@ write_template_prefix (const tree node)
>     add_substitution (substitution);
>   }
>   
> -/* As the list of identifiers for the structured binding declaration
> -   DECL is likely gone, try to recover the DC <source-name>+ E portion
> -   from its mangled name.  Return pointer to the DC and set len to
> -   the length up to and including the terminating E.  On failure
> -   return NULL.  */
> -
> -static const char *
> -find_decomp_unqualified_name (tree decl, size_t *len)
> -{
> -  const char *p = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl));
> -  const char *end = p + IDENTIFIER_LENGTH (DECL_ASSEMBLER_NAME (decl));
> -  bool nested = false;
> -  if (!startswith (p, "_Z"))
> -    return NULL;
> -  p += 2;
> -  if (startswith (p, "St"))
> -    p += 2;
> -  else if (*p == 'N')
> -    {
> -      nested = true;
> -      ++p;
> -      while (ISDIGIT (p[0]))
> -	{
> -	  char *e;
> -	  long num = strtol (p, &e, 10);
> -	  if (num >= 1 && num < end - e)
> -	    p = e + num;
> -	  else
> -	    break;
> -	}
> -    }
> -  if (!startswith (p, "DC"))
> -    return NULL;
> -  if (nested)
> -    {
> -      if (end[-1] != 'E')
> -	return NULL;
> -      --end;
> -    }
> -  if (end[-1] != 'E')
> -    return NULL;
> -  *len = end - p;
> -  return p;
> -}
> -
>   /* "For the purposes of mangling, the name of an anonymous union is considered
>      to be the name of the first named data member found by a pre-order,
>      depth-first, declaration-order walk of the data members of the anonymous
> @@ -1465,17 +1420,7 @@ write_unqualified_name (tree decl)
>       {
>         found = true;
>         gcc_assert (DECL_ASSEMBLER_NAME_SET_P (decl));
> -      const char *decomp_str = NULL;
> -      size_t decomp_len = 0;
> -      if (VAR_P (decl)
> -	  && DECL_DECOMPOSITION_P (decl)
> -	  && DECL_NAME (decl) == NULL_TREE
> -	  && DECL_NAMESPACE_SCOPE_P (decl))
> -	decomp_str = find_decomp_unqualified_name (decl, &decomp_len);
> -      if (decomp_str)
> -	write_chars (decomp_str, decomp_len);
> -      else
> -	write_source_name (DECL_ASSEMBLER_NAME (decl));
> +      write_source_name (DECL_ASSEMBLER_NAME (decl));
>       }
>     else if (DECL_DECLARES_FUNCTION_P (decl))
>       {
> @@ -4373,6 +4318,7 @@ mangle_decomp (const tree decl, vec<tree
>     location_t saved_loc = input_location;
>     input_location = DECL_SOURCE_LOCATION (decl);
>   
> +  check_abi_tags (decl);
>     start_mangling (decl);
>     write_string ("_Z");
>   
> @@ -4380,13 +4326,21 @@ mangle_decomp (const tree decl, vec<tree
>     gcc_assert (context != NULL_TREE);
>   
>     bool nested = false;
> +  bool local = false;
>     if (DECL_NAMESPACE_STD_P (context))
>       write_string ("St");
> +  else if (TREE_CODE (context) == FUNCTION_DECL)
> +    {
> +      local = true;
> +      write_char ('Z');
> +      write_encoding (context);
> +      write_char ('E');
> +    }
>     else if (context != global_namespace)
>       {
>         nested = true;
>         write_char ('N');
> -      write_prefix (decl_mangling_context (decl));
> +      write_prefix (context);
>       }
>   
>     write_string ("DC");
> @@ -4396,8 +4350,22 @@ mangle_decomp (const tree decl, vec<tree
>       write_unqualified_name (d);
>     write_char ('E');
>   
> +  if (tree tags = get_abi_tags (decl))
> +    {
> +      /* We didn't emit ABI tags for structured bindings before ABI 19.  */
> +      if (!G.need_abi_warning
> +	  && TREE_PUBLIC (decl)
> +	  && abi_warn_or_compat_version_crosses (19))
> +	G.need_abi_warning = 1;
> +
> +      if (abi_version_at_least (19))
> +	write_abi_tags (tags);
> +    }
> +
>     if (nested)
>       write_char ('E');
> +  else if (local && DECL_DISCRIMINATOR_P (decl))
> +    write_discriminator (discriminator_for_local_entity (decl));
>   
>     tree id = finish_mangling_get_identifier ();
>     if (DEBUG_MANGLE)
> @@ -4405,6 +4373,37 @@ mangle_decomp (const tree decl, vec<tree
>                IDENTIFIER_POINTER (id));
>   
>     input_location = saved_loc;
> +
> +  if (warn_abi && G.need_abi_warning)
> +    {
> +      const char fabi_version[] = "-fabi-version";
> +      tree id2 = id;
> +      int save_ver = flag_abi_version;
> +
> +      if (flag_abi_version != warn_abi_version)
> +	{
> +	  flag_abi_version = warn_abi_version;
> +	  id2 = mangle_decomp (decl, decls);
> +	  flag_abi_version = save_ver;
> +	}
> +
> +      if (id2 == id)
> +	/* OK.  */;
> +      else if (warn_abi_version != 0
> +	       && abi_version_at_least (warn_abi_version))
> +	warning_at (DECL_SOURCE_LOCATION (G.entity), OPT_Wabi,
> +		    "the mangled name of %qD changed between "
> +		    "%<%s=%d%> (%qD) and %<%s=%d%> (%qD)",
> +		    G.entity, fabi_version, warn_abi_version, id2,
> +		    fabi_version, save_ver, id);
> +      else
> +	warning_at (DECL_SOURCE_LOCATION (G.entity), OPT_Wabi,
> +		    "the mangled name of %qD changes between "
> +		    "%<%s=%d%> (%qD) and %<%s=%d%> (%qD)",
> +		    G.entity, fabi_version, save_ver, id,
> +		    fabi_version, warn_abi_version, id2);
> +    }
> +
>     return id;
>   }
>   
> @@ -4574,6 +4573,13 @@ write_guarded_var_name (const tree varia
>       /* The name of a guard variable for a reference temporary should refer
>          to the reference, not the temporary.  */
>       write_string (IDENTIFIER_POINTER (DECL_NAME (variable)) + 4);
> +  else if (DECL_DECOMPOSITION_P (variable)
> +	   && DECL_NAME (variable) == NULL_TREE
> +	   && startswith (IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (variable)),
> +			  "_Z"))
> +    /* The name of a guard variable for a structured binding needs special
> +       casing.  */
> +    write_string (IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (variable)) + 2);
>     else
>       write_name (variable, /*ignore_local_scope=*/0);
>   }
> @@ -4640,7 +4646,7 @@ mangle_ref_init_variable (const tree var
>     start_mangling (variable);
>     write_string ("_ZGR");
>     check_abi_tags (variable);
> -  write_name (variable, /*ignore_local_scope=*/0);
> +  write_guarded_var_name (variable);
>     /* Avoid name clashes with aggregate initialization of multiple
>        references at once.  */
>     write_compact_number (current_ref_temp_count++);
> --- gcc/cp/parser.cc.jj	2023-08-31 14:31:35.963352093 +0200
> +++ gcc/cp/parser.cc	2023-08-31 20:48:21.127722180 +0200
> @@ -2393,7 +2393,7 @@ static tree cp_parser_c_for
>   static tree cp_parser_range_for
>     (cp_parser *, tree, tree, tree, bool, unsigned short, bool, bool);
>   static void do_range_for_auto_deduction
> -  (tree, tree, tree, unsigned int);
> +  (tree, tree, cp_decomp *);
>   static tree cp_parser_perform_range_for_lookup
>     (tree, tree *, tree *);
>   static tree cp_parser_range_for_member_function
> @@ -13854,8 +13854,7 @@ cp_parser_range_for (cp_parser *parser,
>     tree stmt, range_expr;
>     auto_vec <cxx_binding *, 16> bindings;
>     auto_vec <tree, 16> names;
> -  tree decomp_first_name = NULL_TREE;
> -  unsigned int decomp_cnt = 0;
> +  cp_decomp decomp_d, *decomp = NULL;
>   
>     /* Get the range declaration momentarily out of the way so that
>        the range expression doesn't clash with it. */
> @@ -13872,9 +13871,11 @@ cp_parser_range_for (cp_parser *parser,
>   	    {
>   	      tree d = range_decl;
>   	      range_decl = TREE_OPERAND (v, 0);
> -	      decomp_cnt = tree_to_uhwi (TREE_OPERAND (v, 1)) + 1;
> -	      decomp_first_name = d;
> -	      for (unsigned int i = 0; i < decomp_cnt; i++, d = DECL_CHAIN (d))
> +	      decomp = &decomp_d;
> +	      decomp->count = tree_to_uhwi (TREE_OPERAND (v, 1)) + 1;
> +	      decomp->decl = d;
> +	      for (unsigned int i = 0; i < decomp->count;
> +		   i++, d = DECL_CHAIN (d))
>   		{
>   		  tree name = DECL_NAME (d);
>   		  names.safe_push (name);
> @@ -13928,15 +13929,13 @@ cp_parser_range_for (cp_parser *parser,
>         if (!type_dependent_expression_p (range_expr)
>   	  /* do_auto_deduction doesn't mess with template init-lists.  */
>   	  && !BRACE_ENCLOSED_INITIALIZER_P (range_expr))
> -	do_range_for_auto_deduction (range_decl, range_expr, decomp_first_name,
> -				     decomp_cnt);
> +	do_range_for_auto_deduction (range_decl, range_expr, decomp);
>       }
>     else
>       {
>         stmt = begin_for_stmt (scope, init);
> -      stmt = cp_convert_range_for (stmt, range_decl, range_expr,
> -				   decomp_first_name, decomp_cnt, ivdep,
> -				   unroll, novector);
> +      stmt = cp_convert_range_for (stmt, range_decl, range_expr, decomp,
> +				   ivdep, unroll, novector);
>       }
>     return stmt;
>   }
> @@ -13968,8 +13967,7 @@ build_range_temp (tree range_expr)
>      a shortcut version of cp_convert_range_for.  */
>   
>   static void
> -do_range_for_auto_deduction (tree decl, tree range_expr,
> -			     tree decomp_first_name, unsigned int decomp_cnt)
> +do_range_for_auto_deduction (tree decl, tree range_expr, cp_decomp *decomp)
>   {
>     tree auto_node = type_uses_auto (TREE_TYPE (decl));
>     if (auto_node)
> @@ -13990,7 +13988,7 @@ do_range_for_auto_deduction (tree decl,
>   						tf_warning_or_error,
>   						adc_variable_type);
>   	  if (VAR_P (decl) && DECL_DECOMPOSITION_P (decl))
> -	    cp_finish_decomp (decl, decomp_first_name, decomp_cnt);
> +	    cp_finish_decomp (decl, decomp);
>   	}
>       }
>   }
> @@ -14113,8 +14111,8 @@ warn_for_range_copy (tree decl, tree exp
>   
>   tree
>   cp_convert_range_for (tree statement, tree range_decl, tree range_expr,
> -		      tree decomp_first_name, unsigned int decomp_cnt,
> -		      bool ivdep, unsigned short unroll, bool novector)
> +		      cp_decomp *decomp, bool ivdep, unsigned short unroll,
> +		      bool novector)
>   {
>     tree begin, end;
>     tree iter_type, begin_expr, end_expr;
> @@ -14182,17 +14180,14 @@ cp_convert_range_for (tree statement, tr
>   				     tf_warning_or_error);
>     finish_for_expr (expression, statement);
>   
> -  if (VAR_P (range_decl) && DECL_DECOMPOSITION_P (range_decl))
> -    cp_maybe_mangle_decomp (range_decl, decomp_first_name, decomp_cnt);
> -
>     /* The declaration is initialized with *__begin inside the loop body.  */
>     tree deref_begin = build_x_indirect_ref (input_location, begin, RO_UNARY_STAR,
>   					   NULL_TREE, tf_warning_or_error);
>     cp_finish_decl (range_decl, deref_begin,
>   		  /*is_constant_init*/false, NULL_TREE,
> -		  LOOKUP_ONLYCONVERTING);
> +		  LOOKUP_ONLYCONVERTING, decomp);
>     if (VAR_P (range_decl) && DECL_DECOMPOSITION_P (range_decl))
> -    cp_finish_decomp (range_decl, decomp_first_name, decomp_cnt);
> +    cp_finish_decomp (range_decl, decomp);
>   
>     warn_for_range_copy (range_decl, deref_begin);
>   
> @@ -15890,18 +15885,20 @@ cp_parser_decomposition_declaration (cp_
>   
>         if (decl != error_mark_node)
>   	{
> -	  cp_maybe_mangle_decomp (decl, prev, v.length ());
> +	  cp_decomp decomp = { prev, v.length () };
>   	  cp_finish_decl (decl, initializer, non_constant_p, NULL_TREE,
> -			  (is_direct_init ? LOOKUP_NORMAL : LOOKUP_IMPLICIT));
> -	  cp_finish_decomp (decl, prev, v.length ());
> +			  (is_direct_init ? LOOKUP_NORMAL : LOOKUP_IMPLICIT),
> +			  &decomp);
> +	  cp_finish_decomp (decl, &decomp);
>   	}
>       }
>     else if (decl != error_mark_node)
>       {
>         *maybe_range_for_decl = prev;
> +      cp_decomp decomp = { prev, v.length () };
>         /* Ensure DECL_VALUE_EXPR is created for all the decls but
>   	 the underlying DECL.  */
> -      cp_finish_decomp (decl, prev, v.length ());
> +      cp_finish_decomp (decl, &decomp);
>       }
>   
>     if (pushed_scope)
> @@ -43521,8 +43518,7 @@ cp_convert_omp_range_for (tree &this_pre
>   	  && !BRACE_ENCLOSED_INITIALIZER_P (init))
>   	{
>   	  tree d = decl;
> -	  tree decomp_first_name = NULL_TREE;
> -	  unsigned decomp_cnt = 0;
> +	  cp_decomp decomp_d, *decomp = NULL;
>   	  if (decl != error_mark_node && DECL_HAS_VALUE_EXPR_P (decl))
>   	    {
>   	      tree v = DECL_VALUE_EXPR (decl);
> @@ -43531,11 +43527,12 @@ cp_convert_omp_range_for (tree &this_pre
>   		  && DECL_DECOMPOSITION_P (TREE_OPERAND (v, 0)))
>   		{
>   		  d = TREE_OPERAND (v, 0);
> -		  decomp_cnt = tree_to_uhwi (TREE_OPERAND (v, 1)) + 1;
> -		  decomp_first_name = decl;
> +		  decomp = &decomp_d;
> +		  decomp->count = tree_to_uhwi (TREE_OPERAND (v, 1)) + 1;
> +		  decomp->decl = decl;
>   		}
>   	    }
> -	  do_range_for_auto_deduction (d, init, decomp_first_name, decomp_cnt);
> +	  do_range_for_auto_deduction (d, init, decomp);
>   	}
>         cond = global_namespace;
>         incr = NULL_TREE;
> @@ -43626,8 +43623,7 @@ cp_convert_omp_range_for (tree &this_pre
>     decl = begin;
>     /* Defer popping sl here.  */
>   
> -  tree decomp_first_name = NULL_TREE;
> -  unsigned decomp_cnt = 0;
> +  cp_decomp decomp_d, *decomp = NULL;
>     if (orig_decl != error_mark_node && DECL_HAS_VALUE_EXPR_P (orig_decl))
>       {
>         tree v = DECL_VALUE_EXPR (orig_decl);
> @@ -43637,8 +43633,9 @@ cp_convert_omp_range_for (tree &this_pre
>   	{
>   	  tree d = orig_decl;
>   	  orig_decl = TREE_OPERAND (v, 0);
> -	  decomp_cnt = tree_to_uhwi (TREE_OPERAND (v, 1)) + 1;
> -	  decomp_first_name = d;
> +	  decomp = &decomp_d;
> +	  decomp->count = tree_to_uhwi (TREE_OPERAND (v, 1)) + 1;
> +	  decomp->decl = d;
>   	}
>       }
>   
> @@ -43651,10 +43648,10 @@ cp_convert_omp_range_for (tree &this_pre
>   	{
>   	  TREE_TYPE (orig_decl) = do_auto_deduction (TREE_TYPE (orig_decl),
>   						     t, auto_node);
> -	  if (decomp_first_name)
> +	  if (decomp)
>   	    {
>   	      ++processing_template_decl;
> -	      cp_finish_decomp (orig_decl, decomp_first_name, decomp_cnt);
> +	      cp_finish_decomp (orig_decl, decomp);
>   	      --processing_template_decl;
>   	      if (!processing_template_decl)
>   		clear_has_value_expr = true;
> @@ -43670,8 +43667,9 @@ cp_convert_omp_range_for (tree &this_pre
>        the whole loop nest.  The remaining elements are decls of derived
>        decomposition variables that are bound inside the loop body.  This
>        structure is further mangled by finish_omp_for into the form required
> -     for the OMP_FOR_ORIG_DECLS field of the OMP_FOR tree node.  */
> -  tree v = make_tree_vec (decomp_cnt + 3);
> +     for the OMP_FOR_ORIG_DECLS field of the OMP_FOR tree node.  */\
> +  unsigned decomp_cnt = decomp ? decomp->count : 0;
> +  tree v = make_tree_vec (decomp_cnt);
>     TREE_VEC_ELT (v, 0) = range_temp_decl;
>     TREE_VEC_ELT (v, 1) = end;
>     TREE_VEC_ELT (v, 2) = orig_decl;
> @@ -43686,13 +43684,13 @@ cp_convert_omp_range_for (tree &this_pre
>   	     name but the DECL_VALUE_EXPR will be dependent.  Hide those
>   	     from folding of other loop initializers e.g. for warning
>   	     purposes until cp_finish_omp_range_for.  */
> -	  gcc_checking_assert (DECL_HAS_VALUE_EXPR_P (decomp_first_name)
> -			       || (TREE_TYPE (decomp_first_name)
> +	  gcc_checking_assert (DECL_HAS_VALUE_EXPR_P (decomp->decl)
> +			       || (TREE_TYPE (decomp->decl)
>   				   == error_mark_node));
> -	  DECL_HAS_VALUE_EXPR_P (decomp_first_name) = 0;
> +	  DECL_HAS_VALUE_EXPR_P (decomp->decl) = 0;
>   	}
> -      TREE_VEC_ELT (v, i + 3) = decomp_first_name;
> -      decomp_first_name = DECL_CHAIN (decomp_first_name);
> +      TREE_VEC_ELT (v, i + 3) = decomp->decl;
> +      decomp->decl = DECL_CHAIN (decomp->decl);
>       }
>     orig_decl = tree_cons (NULL_TREE, NULL_TREE, v);
>   }
> @@ -43706,27 +43704,26 @@ cp_finish_omp_range_for (tree orig, tree
>     gcc_assert (TREE_CODE (orig) == TREE_LIST
>   	      && TREE_CODE (TREE_CHAIN (orig)) == TREE_VEC);
>     tree decl = TREE_VEC_ELT (TREE_CHAIN (orig), 2);
> -  tree decomp_first_name = NULL_TREE;
> -  unsigned int decomp_cnt = 0;
> +  cp_decomp decomp_d, *decomp = NULL;
>   
>     if (VAR_P (decl) && DECL_DECOMPOSITION_P (decl))
>       {
> -      decomp_first_name = TREE_VEC_ELT (TREE_CHAIN (orig), 3);
> -      decomp_cnt = TREE_VEC_LENGTH (TREE_CHAIN (orig)) - 3;
> +      decomp = &decomp_d;
> +      decomp_d.decl = TREE_VEC_ELT (TREE_CHAIN (orig), 3);
> +      decomp_d.count = TREE_VEC_LENGTH (TREE_CHAIN (orig)) - 3;
>         if (TREE_PUBLIC (TREE_CHAIN (orig)))
>   	{
>   	  /* Undo temporary clearing of DECL_HAS_VALUE_EXPR_P done
>   	     by cp_convert_omp_range_for above.  */
>   	  TREE_PUBLIC (TREE_CHAIN (orig)) = 0;
> -	  tree d = decomp_first_name;
> -	  for (unsigned i = 0; i < decomp_cnt; i++)
> +	  tree d = decomp_d.decl;
> +	  for (unsigned i = 0; i < decomp_d.count; i++)
>   	    {
>   	      if (TREE_TYPE (d) != error_mark_node)
>   		DECL_HAS_VALUE_EXPR_P (d) = 1;
>   	      d = DECL_CHAIN (d);
>   	    }
>   	}
> -      cp_maybe_mangle_decomp (decl, decomp_first_name, decomp_cnt);
>       }
>   
>     /* The declaration is initialized with *__begin inside the loop body.  */
> @@ -43734,9 +43731,9 @@ cp_finish_omp_range_for (tree orig, tree
>   		  build_x_indirect_ref (input_location, begin, RO_UNARY_STAR,
>   					NULL_TREE, tf_warning_or_error),
>   		  /*is_constant_init*/false, NULL_TREE,
> -		  LOOKUP_ONLYCONVERTING);
> +		  LOOKUP_ONLYCONVERTING, decomp);
>     if (VAR_P (decl) && DECL_DECOMPOSITION_P (decl))
> -    cp_finish_decomp (decl, decomp_first_name, decomp_cnt);
> +    cp_finish_decomp (decl, decomp);
>   }
>   
>   /* Return true if next tokens contain a standard attribute that contains
> --- gcc/cp/pt.cc.jj	2023-08-28 13:55:55.868367710 +0200
> +++ gcc/cp/pt.cc	2023-08-31 20:48:21.127722180 +0200
> @@ -18352,7 +18352,7 @@ tsubst_copy_asm_operands (tree t, tree a
>   static tree *omp_parallel_combined_clauses;
>   
>   static tree tsubst_decomp_names (tree, tree, tree, tsubst_flags_t, tree,
> -				 tree *, unsigned int *);
> +				 cp_decomp *);
>   
>   /* Substitute one OMP_FOR iterator.  */
>   
> @@ -18383,28 +18383,27 @@ tsubst_omp_for_iterator (tree t, int i,
>   	      && VAR_P (TREE_OPERAND (v, 0))
>   	      && DECL_DECOMPOSITION_P (TREE_OPERAND (v, 0)))
>   	    {
> -	      tree decomp_first = NULL_TREE;
> -	      unsigned decomp_cnt = 0;
> +	      cp_decomp decomp_d = { NULL_TREE, 0 };
>   	      tree d = tsubst_decl (TREE_OPERAND (v, 0), args, complain);
>   	      maybe_push_decl (d);
>   	      d = tsubst_decomp_names (d, TREE_OPERAND (v, 0), args, complain,
> -				       in_decl, &decomp_first, &decomp_cnt);
> +				       in_decl, &decomp_d);
>   	      decomp = true;
>   	      if (d == error_mark_node)
>   		decl = error_mark_node;
>   	      else
> -		for (unsigned int i = 0; i < decomp_cnt; i++)
> +		for (unsigned int i = 0; i < decomp_d.count; i++)
>   		  {
> -		    if (!DECL_HAS_VALUE_EXPR_P (decomp_first))
> +		    if (!DECL_HAS_VALUE_EXPR_P (decomp_d.decl))
>   		      {
>   			tree v = build_nt (ARRAY_REF, d,
> -					   size_int (decomp_cnt - i - 1),
> +					   size_int (decomp_d.count - i - 1),
>   					   NULL_TREE, NULL_TREE);
> -			SET_DECL_VALUE_EXPR (decomp_first, v);
> -			DECL_HAS_VALUE_EXPR_P (decomp_first) = 1;
> +			SET_DECL_VALUE_EXPR (decomp_d.decl, v);
> +			DECL_HAS_VALUE_EXPR_P (decomp_d.decl) = 1;
>   		      }
> -		    fit_decomposition_lang_decl (decomp_first, d);
> -		    decomp_first = DECL_CHAIN (decomp_first);
> +		    fit_decomposition_lang_decl (decomp_d.decl, d);
> +		    decomp_d.decl = DECL_CHAIN (decomp_d.decl);
>   		  }
>   	    }
>   	}
> @@ -18723,11 +18722,10 @@ tsubst_find_omp_teams (tree *tp, int *wa
>   
>   static tree
>   tsubst_decomp_names (tree decl, tree pattern_decl, tree args,
> -		     tsubst_flags_t complain, tree in_decl, tree *first,
> -		     unsigned int *cnt)
> +		     tsubst_flags_t complain, tree in_decl, cp_decomp *decomp)
>   {
>     tree decl2, decl3, prev = decl;
> -  *cnt = 0;
> +  decomp->count = 0;
>     gcc_assert (DECL_NAME (decl) == NULL_TREE);
>     for (decl2 = DECL_CHAIN (pattern_decl);
>          decl2
> @@ -18736,12 +18734,12 @@ tsubst_decomp_names (tree decl, tree pat
>          && DECL_NAME (decl2);
>          decl2 = DECL_CHAIN (decl2))
>       {
> -      if (TREE_TYPE (decl2) == error_mark_node && *cnt == 0)
> +      if (TREE_TYPE (decl2) == error_mark_node && decomp->count == 0)
>   	{
>   	  gcc_assert (errorcount);
>   	  return error_mark_node;
>   	}
> -      (*cnt)++;
> +      decomp->count++;
>         gcc_assert (DECL_DECOMP_BASE (decl2) == pattern_decl);
>         gcc_assert (DECL_HAS_VALUE_EXPR_P (decl2));
>         tree v = DECL_VALUE_EXPR (decl2);
> @@ -18771,7 +18769,7 @@ tsubst_decomp_names (tree decl, tree pat
>         else
>   	prev = decl3;
>       }
> -  *first = prev;
> +  decomp->decl = prev;
>     return decl;
>   }
>   
> @@ -19043,8 +19041,8 @@ tsubst_expr (tree t, tree args, tsubst_f
>   		else
>   		  {
>   		    bool const_init = false;
> -		    unsigned int cnt = 0;
> -		    tree first = NULL_TREE, ndecl = error_mark_node;
> +		    cp_decomp decomp_d, *decomp = NULL;
> +		    tree ndecl = error_mark_node;
>   		    tree asmspec_tree = NULL_TREE;
>   		    maybe_push_decl (decl);
>   
> @@ -19056,9 +19054,11 @@ tsubst_expr (tree t, tree args, tsubst_f
>   		    if (VAR_P (decl)
>   			&& DECL_DECOMPOSITION_P (decl)
>   			&& TREE_TYPE (pattern_decl) != error_mark_node)
> -		      ndecl = tsubst_decomp_names (decl, pattern_decl, args,
> -						   complain, in_decl, &first,
> -						   &cnt);
> +		      {
> +			decomp = &decomp_d;
> +			ndecl = tsubst_decomp_names (decl, pattern_decl, args,
> +						     complain, in_decl, decomp);
> +		      }
>   
>   		    init = tsubst_init (init, decl, args, complain, in_decl);
>   
> @@ -19066,9 +19066,6 @@ tsubst_expr (tree t, tree args, tsubst_f
>   		      const_init = (DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P
>   				    (pattern_decl));
>   
> -		    if (ndecl != error_mark_node)
> -		      cp_maybe_mangle_decomp (ndecl, first, cnt);
> -
>   		    /* In a non-template function, VLA type declarations are
>   		       handled in grokdeclarator; for templates, handle them
>   		       now.  */
> @@ -19085,10 +19082,11 @@ tsubst_expr (tree t, tree args, tsubst_f
>   			TREE_TYPE (asmspec_tree) = char_array_type_node;
>   		      }
>   
> -		    cp_finish_decl (decl, init, const_init, asmspec_tree, 0);
> +		    cp_finish_decl (decl, init, const_init, asmspec_tree, 0,
> +				    decomp);
>   
>   		    if (ndecl != error_mark_node)
> -		      cp_finish_decomp (ndecl, first, cnt);
> +		      cp_finish_decomp (ndecl, decomp);
>   		  }
>   	      }
>   	  }
> @@ -19127,12 +19125,13 @@ tsubst_expr (tree t, tree args, tsubst_f
>           maybe_push_decl (decl);
>           expr = RECUR (RANGE_FOR_EXPR (t));
>   
> -	tree decomp_first = NULL_TREE;
> -	unsigned decomp_cnt = 0;
> +	cp_decomp decomp_d, *decomp = NULL;
>   	if (VAR_P (decl) && DECL_DECOMPOSITION_P (decl))
> -	  decl = tsubst_decomp_names (decl, RANGE_FOR_DECL (t), args,
> -				      complain, in_decl,
> -				      &decomp_first, &decomp_cnt);
> +	  {
> +	    decomp = &decomp_d;
> +	    decl = tsubst_decomp_names (decl, RANGE_FOR_DECL (t), args,
> +					complain, in_decl, decomp);
> +	  }
>   
>   	if (processing_template_decl)
>   	  {
> @@ -19140,15 +19139,14 @@ tsubst_expr (tree t, tree args, tsubst_f
>   	    RANGE_FOR_UNROLL (stmt) = RANGE_FOR_UNROLL (t);
>   	    RANGE_FOR_NOVECTOR (stmt) = RANGE_FOR_NOVECTOR (t);
>   	    finish_range_for_decl (stmt, decl, expr);
> -	    if (decomp_first && decl != error_mark_node)
> -	      cp_finish_decomp (decl, decomp_first, decomp_cnt);
> +	    if (decomp && decl != error_mark_node)
> +	      cp_finish_decomp (decl, decomp);
>   	  }
>   	else
>   	  {
>   	    unsigned short unroll = (RANGE_FOR_UNROLL (t)
>   				     ? tree_to_uhwi (RANGE_FOR_UNROLL (t)) : 0);
> -	    stmt = cp_convert_range_for (stmt, decl, expr,
> -					 decomp_first, decomp_cnt,
> +	    stmt = cp_convert_range_for (stmt, decl, expr, decomp,
>   					 RANGE_FOR_IVDEP (t), unroll,
>   					 RANGE_FOR_NOVECTOR (t));
>   	  }
> --- gcc/testsuite/g++.dg/cpp2a/decomp8.C.jj	2023-08-31 19:53:31.205280379 +0200
> +++ gcc/testsuite/g++.dg/cpp2a/decomp8.C	2023-08-31 19:53:31.205280379 +0200
> @@ -0,0 +1,74 @@
> +// PR c++/111069
> +// { dg-do compile { target c++11 } }
> +// { dg-options "" }
> +
> +extern int a[2];
> +struct Y { int b, c, d; };
> +
> +inline int
> +freddy ()
> +{
> +  static auto [i, j] = a;		// { dg-warning "structured bindings only available with" "" { target c++14_down } }
> +					// { dg-warning "structured binding declaration can be 'static' only in" "" { target c++17_down } .-1 }
> +  static auto [k, l] = a;		// { dg-warning "structured bindings only available with" "" { target c++14_down } }
> +					// { dg-warning "structured binding declaration can be 'static' only in" "" { target c++17_down } .-1 }
> +  int ret = ++i + ++k;
> +  {
> +    static auto [i, j] = a;		// { dg-warning "structured bindings only available with" "" { target c++14_down } }
> +					// { dg-warning "structured binding declaration can be 'static' only in" "" { target c++17_down } .-1 }
> +    static auto [k, l] = a;		// { dg-warning "structured bindings only available with" "" { target c++14_down } }
> +					// { dg-warning "structured binding declaration can be 'static' only in" "" { target c++17_down } .-1 }
> +    ret += ++i + ++k;
> +  }
> +  {
> +    static auto [i, j] = a;		// { dg-warning "structured bindings only available with" "" { target c++14_down } }
> +					// { dg-warning "structured binding declaration can be 'static' only in" "" { target c++17_down } .-1 }
> +    static auto [k, l] = a;		// { dg-warning "structured bindings only available with" "" { target c++14_down } }
> +					// { dg-warning "structured binding declaration can be 'static' only in" "" { target c++17_down } .-1 }
> +    ret += ++i + ++k;
> +  }
> +  return ret;
> +}
> +
> +namespace N
> +{
> +  namespace M
> +  {
> +    template <int N>
> +    inline int
> +    corge ()
> +    {
> +      static auto [i, j] = a;		// { dg-warning "structured bindings only available with" "" { target c++14_down } }
> +					// { dg-warning "structured binding declaration can be 'static' only in" "" { target c++17_down } .-1 }
> +      static auto && [u, v, w] = Y{};	// { dg-warning "structured bindings only available with" "" { target c++14_down } }
> +					// { dg-warning "structured binding declaration can be 'static' only in" "" { target c++17_down } .-1 }
> +      int ret = ++i + ++u;
> +      {
> +	static auto && [u, v, w] = Y{};	// { dg-warning "structured bindings only available with" "" { target c++14_down } }
> +					// { dg-warning "structured binding declaration can be 'static' only in" "" { target c++17_down } .-1 }
> +	ret += ++v;
> +      }
> +      return ret;
> +    }
> +  }
> +}
> +
> +int (*p) () = &freddy;
> +int (*q) () = N::M::corge<3>;
> +
> +// { dg-final { scan-assembler "_ZZ6freddyvEDC1i1jE" } }
> +// { dg-final { scan-assembler "_ZZ6freddyvEDC1i1jE_0" } }
> +// { dg-final { scan-assembler "_ZZ6freddyvEDC1i1jE_1" } }
> +// { dg-final { scan-assembler "_ZZ6freddyvEDC1k1lE" } }
> +// { dg-final { scan-assembler "_ZZ6freddyvEDC1k1lE_0" } }
> +// { dg-final { scan-assembler "_ZZ6freddyvEDC1k1lE_1" } }
> +// { dg-final { scan-assembler "_ZZN1N1M5corgeILi3EEEivEDC1i1jE" } }
> +// { dg-final { scan-assembler "_ZGVZ6freddyvEDC1i1jE" } }
> +// { dg-final { scan-assembler "_ZGVZ6freddyvEDC1i1jE_0" } }
> +// { dg-final { scan-assembler "_ZGVZ6freddyvEDC1i1jE_1" } }
> +// { dg-final { scan-assembler "_ZGVZ6freddyvEDC1k1lE" } }
> +// { dg-final { scan-assembler "_ZGVZ6freddyvEDC1k1lE_0" } }
> +// { dg-final { scan-assembler "_ZGVZ6freddyvEDC1k1lE_1" } }
> +// { dg-final { scan-assembler "_ZGVZN1N1M5corgeILi3EEEivEDC1i1jE" } }
> +// { dg-final { scan-assembler "_ZGRZN1N1M5corgeILi3EEEivEDC1u1v1wE_" } }
> +// { dg-final { scan-assembler "_ZGRZN1N1M5corgeILi3EEEivEDC1u1v1wE_0_" } }
> --- gcc/testsuite/g++.dg/cpp2a/decomp9.C.jj	2023-08-31 19:53:31.205280379 +0200
> +++ gcc/testsuite/g++.dg/cpp2a/decomp9.C	2023-08-31 19:53:31.205280379 +0200
> @@ -0,0 +1,82 @@
> +// PR c++/111069
> +// { dg-do compile { target c++11 } }
> +// { dg-options "" }
> +
> +struct [[gnu::abi_tag ("foobar")]] S { int i; };
> +extern S a[2];
> +struct [[gnu::abi_tag ("qux")]] T { int i; S j; int k; };
> +extern T b[2];
> +
> +namespace N {
> +  auto [i, j] = a;		// { dg-warning "structured bindings only available with" "" { target c++14_down } }
> +  auto [k, l] = b;		// { dg-warning "structured bindings only available with" "" { target c++14_down } }
> +}
> +
> +inline int
> +foo ()
> +{
> +  static auto [m, n] = a;	// { dg-warning "structured bindings only available with" "" { target c++14_down } }
> +				// { dg-warning "structured binding declaration can be 'static' only in" "" { target c++17_down } .-1 }
> +  static auto [o, p] = b;	// { dg-warning "structured bindings only available with" "" { target c++14_down } }
> +				// { dg-warning "structured binding declaration can be 'static' only in" "" { target c++17_down } .-1 }
> +  int ret = ++N::i.i + ++N::k.i;
> +  {
> +    static auto [m, n] = a;	// { dg-warning "structured bindings only available with" "" { target c++14_down } }
> +				// { dg-warning "structured binding declaration can be 'static' only in" "" { target c++17_down } .-1 }
> +
> +    ret += ++n.i;
> +  }
> +  {
> +    static auto [m, n] = a;	// { dg-warning "structured bindings only available with" "" { target c++14_down } }
> +				// { dg-warning "structured binding declaration can be 'static' only in" "" { target c++17_down } .-1 }
> +
> +    ret += ++n.i;
> +  }
> +  ret += ++m.i + ++o.i;
> +  return ret;
> +}
> +
> +template <typename T>
> +inline int
> +bar ()
> +{
> +  static auto [m, n] = a;	// { dg-warning "structured bindings only available with" "" { target c++14_down } }
> +				// { dg-warning "structured binding declaration can be 'static' only in" "" { target c++17_down } .-1 }
> +  static auto [o, p] = b;	// { dg-warning "structured bindings only available with" "" { target c++14_down } }
> +				// { dg-warning "structured binding declaration can be 'static' only in" "" { target c++17_down } .-1 }
> +  int ret = 0;
> +  {
> +    static auto [m, n] = a;	// { dg-warning "structured bindings only available with" "" { target c++14_down } }
> +				// { dg-warning "structured binding declaration can be 'static' only in" "" { target c++17_down } .-1 }
> +    ret += ++n.i;
> +  }
> +  {
> +    static auto [m, n] = a;	// { dg-warning "structured bindings only available with" "" { target c++14_down } }
> +				// { dg-warning "structured binding declaration can be 'static' only in" "" { target c++17_down } .-1 }
> +    ret += ++n.i;
> +  }
> +  ret += ++m.i + ++o.i;
> +  return ret;
> +}
> +
> +int (*p) () = &foo;
> +int (*q) () = &bar<T>;
> +
> +// { dg-final { scan-assembler "_ZZ3foovEDC1m1nEB6foobar" } }
> +// { dg-final { scan-assembler "_ZZ3foovEDC1m1nEB6foobar_0" } }
> +// { dg-final { scan-assembler "_ZZ3foovEDC1m1nEB6foobar_1" } }
> +// { dg-final { scan-assembler "_ZZ3foovEDC1o1pEB3qux" } }
> +// { dg-final { scan-assembler "_ZZ3barI1TB3quxEivEDC1m1nEB6foobar" } }
> +// { dg-final { scan-assembler "_ZZ3barI1TB3quxEivEDC1m1nEB6foobar_0" } }
> +// { dg-final { scan-assembler "_ZZ3barI1TB3quxEivEDC1m1nEB6foobar_1" } }
> +// { dg-final { scan-assembler "_ZZ3barI1TB3quxEivEDC1o1pEB3qux" } }
> +// { dg-final { scan-assembler "_ZN1NDC1i1jEB6foobarE" } }
> +// { dg-final { scan-assembler "_ZN1NDC1k1lEB3quxE" } }
> +// { dg-final { scan-assembler "_ZGVZ3foovEDC1m1nEB6foobar" } }
> +// { dg-final { scan-assembler "_ZGVZ3foovEDC1m1nEB6foobar_0" } }
> +// { dg-final { scan-assembler "_ZGVZ3foovEDC1m1nEB6foobar_1" } }
> +// { dg-final { scan-assembler "_ZGVZ3foovEDC1o1pEB3qux" } }
> +// { dg-final { scan-assembler "_ZGVZ3barI1TB3quxEivEDC1m1nEB6foobar" } }
> +// { dg-final { scan-assembler "_ZGVZ3barI1TB3quxEivEDC1m1nEB6foobar_0" } }
> +// { dg-final { scan-assembler "_ZGVZ3barI1TB3quxEivEDC1m1nEB6foobar_1" } }
> +// { dg-final { scan-assembler "_ZGVZ3barI1TB3quxEivEDC1o1pEB3qux" } }
> --- gcc/testsuite/g++.dg/abi/macro0.C.jj	2022-10-11 10:00:07.456124822 +0200
> +++ gcc/testsuite/g++.dg/abi/macro0.C	2023-08-31 19:53:31.222280146 +0200
> @@ -1,6 +1,6 @@
>   // This testcase will need to be kept in sync with c_common_post_options.
>   // { dg-options "-fabi-version=0" }
>   
> -#if __GXX_ABI_VERSION != 1018
> +#if __GXX_ABI_VERSION != 1019
>   #error "Incorrect value of __GXX_ABI_VERSION"
>   #endif
> 
> 
> 	Jakub
> 


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

end of thread, other threads:[~2023-08-31 19:41 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-08-22  8:12 [PATCH] c++: Fix up mangling of function/block scope static structured bindings [PR111069] Jakub Jelinek
2023-08-23 20:23 ` Jason Merrill
2023-08-24 16:39   ` Jakub Jelinek
2023-08-28 13:58     ` [PATCH] c++, v2: Fix up mangling of function/block scope static structured bindings and emit abi tags [PR111069] Jakub Jelinek
2023-08-31 17:11       ` Jason Merrill
2023-08-31 19:14         ` [PATCH] c++, v3: " Jakub Jelinek
2023-08-31 19:41           ` Jason Merrill

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).