c++, v2: Fix up mangling of function/block scope static structured bindings and emit abi tags [PR111069]

Message ID ZOyn+6C9ImVbp+TA@tucnak
State Unresolved
Headers
Series c++, v2: Fix up mangling of function/block scope static structured bindings and emit abi tags [PR111069] |

Checks

Context Check Description
snail/gcc-patch-check warning Git am fail log

Commit Message

Jakub Jelinek Aug. 28, 2023, 1:58 p.m. UTC
  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.




	Jakub
  

Comments

Jason Merrill Aug. 31, 2023, 5:11 p.m. UTC | #1
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
  

Patch

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