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

Message ID ZPDmi9mJYR6l8cJx@tucnak
State Unresolved
Headers
Series c++, v3: 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. 31, 2023, 7:14 p.m. UTC
  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.



	Jakub
  

Comments

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

Patch

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