]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
c++: Fix up mangling of function/block scope static structured bindings [PR111069]
authorJakub Jelinek <jakub@redhat.com>
Fri, 1 Sep 2023 13:07:48 +0000 (15:07 +0200)
committerJakub Jelinek <jakub@redhat.com>
Fri, 1 Sep 2023 13:07:48 +0000 (15:07 +0200)
As can be seen on the testcase, we weren't correctly mangling
static/thread_local structured bindings (C++20 feature) at function/block
scope.  The following patch fixes that by using what write_local_name
does for those cases (note, structured binding mandling doesn't use the
standard path because it needs to pass a list of all the identifiers in
the structured binding to the mangling).  In addition to that it fixes
mangling of various helpers which use write_guarded_name (_ZGV*, _ZTH*,
_ZTW*) and kills find_decomp_unqualified_name which for the local names
would be too hard to implement and uses write_guarded_name for structured
binding related _ZGR* names as well.

All the mangled names on the first testcase match now clang++ and my
expectations.
Because the old mangled names were plain wrong (they mangled the same as
structured binding at global scope and resulted in assembly errors if there
was more than one static structured binding with the same identifiers in
the same (or another) function, I think we don't need to play with another
mangling ABI level which turns on/off the old broken way.

In addition to that the patch starts to emit abi-tags into the mangle_decomp
produced names when needed and emits a -Wabi warning for that as well.
To make that work, I had to move cp_maybe_mangle_decomp calls from before
cp_finish_decl into a middle of cp_finish_decl after type is deduced and
maybe_commonize_var (which also had to be changed not to ignore structured
bindings) is called but before anything might need a mangled name for the
decl, so a new cp_decomp structure is passed to cp_finish_decl; various
other structured binding related functions have been changed to pass
pointer to that around instead of passing a tree and unsigned int separately.

On decomp9.C, there is a
_ZZ3barI1TB3quxEivEDC1o1pEB3qux
(g++) vs.
_ZZ3barI1TB3quxEivEDC1o1pE
(clang++) mangling difference, but that seems to be a clang++ bug and happens
also with normal static block vars, doesn't need structured bindings.

2023-09-01  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/c-family/c-opts.cc
gcc/common.opt
gcc/cp/cp-tree.h
gcc/cp/decl.cc
gcc/cp/mangle.cc
gcc/cp/parser.cc
gcc/cp/pt.cc
gcc/doc/invoke.texi
gcc/testsuite/g++.dg/abi/macro0.C
gcc/testsuite/g++.dg/cpp2a/decomp8.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/decomp9.C [new file with mode: 0644]

index 4961af63de82fe96daabdfc6ca65fe8efdd2cc40..d9f55f45e030fd50a273760023ee7f632e5baacb 100644 (file)
@@ -974,7 +974,7 @@ c_common_post_options (const char **pfilename)
 
   /* 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;
 
index 0888c15b88f03c83f0d0b264b75814e85afee1d6..3e1939293e8f44173934ed805033a694ad5a58ad 100644 (file)
@@ -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=
index 6b225ca182f3ed0b3359cf1dbc97f454669e442a..3ca011c61c8a1c8716877b63cf97456e7140c78c 100644 (file)
@@ -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_declarator *, cp_decl_specifier_seq *, int,
 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 &);
index a0e8a24efc0eda0ebc4c14e01eef6802aa370dd7..89e8b85e3f8931f2e377ea6acf006913ae68c5a9 100644 (file)
@@ -911,15 +911,16 @@ static GTY((deletable)) vec<tree, va_gc> *local_entities;
    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, tree attr)
     }
 }
 
+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, tree attr)
    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, bool init_const_expr_p,
          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 first, unsigned int count)
    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:
index bb0e9d38203a5104f098098652f798eb01571421..d88c779bfa2e3ed41a73e92c0f117c3dcce6375e 100644 (file)
@@ -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> &decls)
   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> &decls)
   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> &decls)
     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> &decls)
              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 variable)
     /* 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 variable)
   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++);
index 86c0bb13428e469df33f2290a0b9c2e654728553..7811d582b072a66e683dcf547e4437a20b5503fa 100644 (file)
@@ -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 scope, tree init, tree range_decl,
   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 scope, tree init, tree range_decl,
            {
              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, tree scope, tree init, tree range_decl,
       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, tree range_expr,
                                                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 expr)
 
 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, tree range_decl, tree range_expr,
                                     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_parser *parser,
 
       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_body, tree &sl,
          && !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_body, tree &sl,
                  && 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_body, tree &sl,
   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_body, tree &sl,
        {
          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_body, tree &sl,
        {
          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,7 +43667,8 @@ cp_convert_omp_range_for (tree &this_pre_body, tree &sl,
      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.  */
+     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 + 3);
   TREE_VEC_ELT (v, 0) = range_temp_decl;
   TREE_VEC_ELT (v, 1) = end;
@@ -43686,13 +43684,13 @@ cp_convert_omp_range_for (tree &this_pre_body, tree &sl,
             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 begin)
   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 begin)
                  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
index eaa7adb51dc6c526dd1e086f42bfbec5fbe4a3f9..0790bbf948ff20ca3ded92f19cafcc6be935b111 100644 (file)
@@ -18352,7 +18352,7 @@ tsubst_copy_asm_operands (tree t, tree args, tsubst_flags_t complain,
 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, tree declv, tree &orig_declv,
              && 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 *walk_subtrees, void *)
 
 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 pattern_decl, tree args,
        && 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 pattern_decl, tree args,
       else
        prev = decl3;
     }
-  *first = prev;
+  decomp->decl = prev;
   return decl;
 }
 
@@ -19043,8 +19041,8 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl)
                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_flags_t complain, tree in_decl)
                    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_flags_t complain, tree in_decl)
                      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_flags_t complain, tree in_decl)
                        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_flags_t complain, tree in_decl)
         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_flags_t complain, tree in_decl)
            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));
          }
index 06941d79cd81b91ef908b9df2c569a60e60417a6..02de0f657e858f37085b68626ad8c07f3cb263e5 100644 (file)
@@ -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
index 4a0e9d06ca0f846c3386c145dcadf0b04fee30f8..183184e0f0a3c0ae265dcbcbbfa19076b27344d8 100644 (file)
@@ -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
diff --git a/gcc/testsuite/g++.dg/cpp2a/decomp8.C b/gcc/testsuite/g++.dg/cpp2a/decomp8.C
new file mode 100644 (file)
index 0000000..6919130
--- /dev/null
@@ -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_" } }
diff --git a/gcc/testsuite/g++.dg/cpp2a/decomp9.C b/gcc/testsuite/g++.dg/cpp2a/decomp9.C
new file mode 100644 (file)
index 0000000..e8ab0b7
--- /dev/null
@@ -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" } }