Hi!

https://eel.is/c++draft/stmt.ranged#2
says that in for-range-declaration only type-specifier or constexpr
can appear.  As the following testcases show, we've emitted some
diagnostics in most cases, but not for static/thread_local (the patch
handles __thread too) and register in the non-sb case.
For extern there was an error that it is both extern and has an
initializer (again, non-sb only, sb errors on extern).

The following patch diagnoses those cases with pedwarn.
I've used for-range-declaration in the diagnostics wording (there was
already a case of that for the typedef), so that in the future
we don't need to differentiate it between range for and expansion
statements.

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

2025-07-05  Jakub Jelinek  <ja...@redhat.com>

        PR c++/84009
        * parser.cc (cp_parser_decomposition_declaration): Pedwarn
        on thread_local, __thread or static in decl_specifiers for
        for-range-declaration.
        (cp_parser_init_declarator): Likewise, and also for extern
        or register.

        * g++.dg/cpp0x/range-for40.C: New test.
        * g++.dg/cpp0x/range-for41.C: New test.
        * g++.dg/cpp0x/range-for42.C: New test.
        * g++.dg/cpp0x/range-for43.C: New test.

--- gcc/cp/parser.cc.jj 2025-07-04 19:49:14.702864248 +0200
+++ gcc/cp/parser.cc    2025-07-04 21:29:46.568232075 +0200
@@ -16919,6 +16919,15 @@ cp_parser_decomposition_declaration (cp_
       /* Ensure DECL_VALUE_EXPR is created for all the decls but
         the underlying DECL.  */
       cp_finish_decomp (decl, &decomp);
+      if (decl_spec_seq_has_spec_p (decl_specifiers, ds_thread))
+       pedwarn (decl_specifiers->locations[ds_thread],
+                OPT_Wpedantic, "for-range-declaration cannot be %qs",
+                decl_specifiers->gnu_thread_keyword_p
+                ? "__thread" : "thread_local");
+      else if (decl_specifiers->storage_class == sc_static)
+       pedwarn (decl_specifiers->locations[ds_storage_class],
+                OPT_Wpedantic, "for-range-declaration cannot be %qs",
+                "static");
     }
 
   if (pushed_scope)
@@ -24162,7 +24171,26 @@ cp_parser_init_declarator (cp_parser* pa
          && token->type != CPP_SEMICOLON)
        {
          if (maybe_range_for_decl && *maybe_range_for_decl != error_mark_node)
-           range_for_decl_p = true;
+           {
+             range_for_decl_p = true;
+             if (decl_spec_seq_has_spec_p (decl_specifiers, ds_thread))
+               pedwarn (decl_specifiers->locations[ds_thread],
+                        OPT_Wpedantic, "for-range-declaration cannot be %qs",
+                        decl_specifiers->gnu_thread_keyword_p
+                        ? "__thread" : "thread_local");
+             else if (decl_specifiers->storage_class == sc_static)
+               pedwarn (decl_specifiers->locations[ds_storage_class],
+                        OPT_Wpedantic, "for-range-declaration cannot be %qs",
+                        "static");
+             else if (decl_specifiers->storage_class == sc_extern)
+               pedwarn (decl_specifiers->locations[ds_storage_class],
+                        OPT_Wpedantic, "for-range-declaration cannot be %qs",
+                        "extern");
+             else if (decl_specifiers->storage_class == sc_register)
+               pedwarn (decl_specifiers->locations[ds_storage_class],
+                        OPT_Wpedantic, "for-range-declaration cannot be %qs",
+                        "register");
+           }
          else
            {
              if (!maybe_range_for_decl)
--- gcc/testsuite/g++.dg/cpp0x/range-for40.C.jj 2025-07-04 21:03:00.951729318 
+0200
+++ gcc/testsuite/g++.dg/cpp0x/range-for40.C    2025-07-04 21:11:29.426240713 
+0200
@@ -0,0 +1,41 @@
+// PR c++/84009
+// { dg-do compile { target c++11 } }
+
+int z[64];
+
+void
+foo ()
+{
+  for (static auto a : z)              // { dg-error "for-range-declaration 
cannot be 'static'" }
+    ;
+  for (thread_local auto a : z)                // { dg-error 
"for-range-declaration cannot be 'thread_local'" }
+    ;
+  for (__thread auto a : z)            // { dg-error "for-range-declaration 
cannot be '__thread'" }
+    ;                                  // { dg-error "function-scope 'a' 
implicitly auto and declared '__thread'" "" { target *-*-* } .-1 }
+  for (register auto a : z)            // { dg-error "for-range-declaration 
cannot be 'register'" }
+    ;                                  // { dg-error "does not allow 
'register' storage class specifier" "" { target c++17 } .-1 }
+  for (extern auto a : z)              // { dg-error "for-range-declaration 
cannot be 'extern'" }
+    ;                                  // { dg-error "'a' has both 'extern' 
and initializer" "" { target *-*-* } .-1 }
+  for (mutable auto a : z)             // { dg-error "non-member 'a' cannot be 
declared 'mutable'" }
+    ;
+  for (virtual auto a : z)             // { dg-error "'virtual' outside class 
declaration" }
+    ;
+  for (explicit auto a : z)            // { dg-error "'explicit' outside class 
declaration" }
+    ;
+  for (friend auto a : z)              // { dg-error "'friend' used outside of 
class" }
+    ;
+  for (typedef auto a : z)             // { dg-error "typedef declared 'auto'" 
}
+    ;                                  // { dg-error "typedef 'a' is 
initialized \\\(use 'decltype' instead\\\)" "" { target *-*-* } .-1 }
+#if __cplusplus >= 202002L
+  for (consteval auto a : z)           // { dg-error "a variable cannot be 
declared 'consteval'" "" { target c++20 } }
+    ;
+  for (constinit auto a : z)           // { dg-error "'constinit' can only be 
applied to a variable with static or thread storage duration" "" { target c++20 
} }
+    ;
+#endif
+  for (inline auto a : z)              // { dg-error "'inline' specifier 
invalid for variable 'a' declared at block scope" }
+    ;
+  for (struct S { int a; } a : z)      // { dg-error "types may not be defined 
in a for-range-declaration" }
+    ;                                  // { dg-error "conversion from 'int' to 
non-scalar type 'foo\\\(\\\)::S' requested" "" { target *-*-* } .-1 }
+  for (enum E { E0 } a : z)            // { dg-error "types may not be defined 
in a for-range-declaration" }
+    ;                                  // { dg-error "invalid conversion from 
'int' to 'foo\\\(\\\)::E'" "" { target *-*-* } .-1 }
+}
--- gcc/testsuite/g++.dg/cpp0x/range-for41.C.jj 2025-07-04 21:12:17.765623538 
+0200
+++ gcc/testsuite/g++.dg/cpp0x/range-for41.C    2025-07-04 21:17:07.796920537 
+0200
@@ -0,0 +1,42 @@
+// PR c++/84009
+// { dg-do compile { target c++11 } }
+// { dg-options "" }
+
+int z[64];
+
+void
+foo ()
+{
+  for (static auto a : z)
+    ;
+  for (thread_local auto a : z)
+    ;
+  for (__thread auto a : z)            // { dg-warning "function-scope 'a' 
implicitly auto and declared '__thread'" }
+    ;
+  for (register auto a : z)            // { dg-warning "does not allow 
'register' storage class specifier" "" { target c++17 } }
+    ;
+  for (extern auto a : z)
+    ;                                  // { dg-error "'a' has both 'extern' 
and initializer" "" { target *-*-* } .-1 }
+  for (mutable auto a : z)             // { dg-error "non-member 'a' cannot be 
declared 'mutable'" }
+    ;
+  for (virtual auto a : z)             // { dg-error "'virtual' outside class 
declaration" }
+    ;
+  for (explicit auto a : z)            // { dg-error "'explicit' outside class 
declaration" }
+    ;
+  for (friend auto a : z)              // { dg-error "'friend' used outside of 
class" }
+    ;
+  for (typedef auto a : z)             // { dg-error "typedef declared 'auto'" 
}
+    ;                                  // { dg-error "typedef 'a' is 
initialized \\\(use 'decltype' instead\\\)" "" { target *-*-* } .-1 }
+#if __cplusplus >= 202002L
+  for (consteval auto a : z)           // { dg-error "a variable cannot be 
declared 'consteval'" "" { target c++20 } }
+    ;
+  for (constinit auto a : z)           // { dg-error "'constinit' can only be 
applied to a variable with static or thread storage duration" "" { target c++20 
} }
+    ;
+#endif
+  for (inline auto a : z)              // { dg-error "'inline' specifier 
invalid for variable 'a' declared at block scope" }
+    ;
+  for (struct S { int a; } a : z)      // { dg-error "types may not be defined 
in a for-range-declaration" }
+    ;                                  // { dg-error "conversion from 'int' to 
non-scalar type 'foo\\\(\\\)::S' requested" "" { target *-*-* } .-1 }
+  for (enum E { E0 } a : z)            // { dg-error "types may not be defined 
in a for-range-declaration" }
+    ;                                  // { dg-error "invalid conversion from 
'int' to 'foo\\\(\\\)::E'" "" { target *-*-* } .-1 }
+}
--- gcc/testsuite/g++.dg/cpp0x/range-for42.C.jj 2025-07-04 21:18:01.921229498 
+0200
+++ gcc/testsuite/g++.dg/cpp0x/range-for42.C    2025-07-04 21:56:40.358626480 
+0200
@@ -0,0 +1,41 @@
+// PR c++/84009
+// { dg-do compile { target c++11 } }
+
+struct S { int y; } z[64];
+
+void
+foo ()
+{
+  for (static auto [ a ] : z)          // { dg-error "for-range-declaration 
cannot be 'static'" }
+    ;                                  // { dg-error "structured binding 
declaration can be 'static' only in" "" { target c++17_down } .-1 }
+                                       // { dg-error "structured bindings only 
available with" "" { target c++14_down } .-2 }
+  for (thread_local auto [ a ] : z)    // { dg-error "for-range-declaration 
cannot be 'thread_local'" }
+    ;                                  // { dg-error "structured binding 
declaration can be 'thread_local' only in" "" { target c++17_down } .-1 }
+                                       // { dg-error "structured bindings only 
available with" "" { target c++14_down } .-2 }
+  for (__thread auto [ a ] : z)                // { dg-error 
"for-range-declaration cannot be '__thread'" }
+    ;                                  // { dg-error "function-scope 
'structured binding' implicitly auto and declared '__thread'" "" { target *-*-* 
} .-1 }
+                                       // { dg-error "structured binding 
declaration can be '__thread' only in" "" { target c++17_down } .-2 }
+                                       // { dg-error "structured bindings only 
available with" "" { target c++14_down } .-3 }
+  for (register auto [ a ] : z)                // { dg-error "structured 
binding declaration cannot be 'register'" }
+    ;                                  // { dg-error "structured bindings only 
available with" "" { target c++14_down } .-1 }
+  for (extern auto [ a ] : z)          // { dg-error "structured binding 
declaration cannot be 'extern'" }
+    ;                                  // { dg-error "structured bindings only 
available with" "" { target c++14_down } .-1 }
+  for (mutable auto [ a ] : z)         // { dg-error "structured binding 
declaration cannot be 'mutable'" }
+    ;                                  // { dg-error "structured bindings only 
available with" "" { target c++14_down } .-1 }
+  for (virtual auto [ a ] : z)         // { dg-error "'virtual' outside class 
declaration" }
+    ;                                  // { dg-error "structured bindings only 
available with" "" { target c++14_down } .-1 }
+  for (explicit auto [ a ] : z)                // { dg-error "'explicit' 
outside class declaration" }
+    ;                                  // { dg-error "structured bindings only 
available with" "" { target c++14_down } .-1 }
+  for (friend auto [ a ] : z)          // { dg-error "'friend' used outside of 
class" }
+    ;                                  // { dg-error "structured bindings only 
available with" "" { target c++14_down } .-1 }
+  for (typedef auto [ a ] : z)         // { dg-error "structured binding 
declaration cannot be 'typedef'" }
+    ;                                  // { dg-error "structured bindings only 
available with" "" { target c++14_down } .-1 }
+#if __cplusplus >= 202002L
+  for (consteval auto [ a ] : z)       // { dg-error "structured binding 
declaration cannot be 'consteval'" "" { target c++20 } }
+    ;
+  for (constinit auto [ a ] : z)       // { dg-error "'constinit' can only be 
applied to a variable with static or thread storage duration" "" { target c++20 
} }
+    ;
+#endif
+  for (inline auto [ a ] : z)          // { dg-error "structured binding 
declaration cannot be 'inline'" }
+    ;                                  // { dg-error "structured bindings only 
available with" "" { target c++14_down } .-1 }
+}
--- gcc/testsuite/g++.dg/cpp0x/range-for43.C.jj 2025-07-04 21:56:56.615418951 
+0200
+++ gcc/testsuite/g++.dg/cpp0x/range-for43.C    2025-07-04 22:00:25.885747537 
+0200
@@ -0,0 +1,39 @@
+// PR c++/84009
+// { dg-do compile { target c++11 } }
+// { dg-options "" }
+
+struct S { int y; } z[64];
+
+void
+foo ()
+{
+  for (static auto [ a ] : z)          // { dg-warning "structured binding 
declaration can be 'static' only in" "" { target c++17_down } }
+    ;                                  // { dg-warning "structured bindings 
only available with" "" { target c++14_down } .-1 }
+  for (thread_local auto [ a ] : z)    // { dg-warning "structured binding 
declaration can be 'thread_local' only in" "" { target c++17_down } }
+    ;                                  // { dg-warning "structured bindings 
only available with" "" { target c++14_down } .-1 }
+  for (__thread auto [ a ] : z)                // { dg-warning "function-scope 
'structured binding' implicitly auto and declared '__thread'" }
+    ;                                  // { dg-warning "structured binding 
declaration can be '__thread' only in" "" { target c++17_down } .-1 }
+                                       // { dg-warning "structured bindings 
only available with" "" { target c++14_down } .-2 }
+  for (register auto [ a ] : z)                // { dg-error "structured 
binding declaration cannot be 'register'" }
+    ;                                  // { dg-warning "structured bindings 
only available with" "" { target c++14_down } .-1 }
+  for (extern auto [ a ] : z)          // { dg-error "structured binding 
declaration cannot be 'extern'" }
+    ;                                  // { dg-warning "structured bindings 
only available with" "" { target c++14_down } .-1 }
+  for (mutable auto [ a ] : z)         // { dg-error "structured binding 
declaration cannot be 'mutable'" }
+    ;                                  // { dg-warning "structured bindings 
only available with" "" { target c++14_down } .-1 }
+  for (virtual auto [ a ] : z)         // { dg-error "'virtual' outside class 
declaration" }
+    ;                                  // { dg-warning "structured bindings 
only available with" "" { target c++14_down } .-1 }
+  for (explicit auto [ a ] : z)                // { dg-error "'explicit' 
outside class declaration" }
+    ;                                  // { dg-warning "structured bindings 
only available with" "" { target c++14_down } .-1 }
+  for (friend auto [ a ] : z)          // { dg-error "'friend' used outside of 
class" }
+    ;                                  // { dg-warning "structured bindings 
only available with" "" { target c++14_down } .-1 }
+  for (typedef auto [ a ] : z)         // { dg-error "structured binding 
declaration cannot be 'typedef'" }
+    ;                                  // { dg-warning "structured bindings 
only available with" "" { target c++14_down } .-1 }
+#if __cplusplus >= 202002L
+  for (consteval auto [ a ] : z)       // { dg-error "structured binding 
declaration cannot be 'consteval'" "" { target c++20 } }
+    ;
+  for (constinit auto [ a ] : z)       // { dg-error "'constinit' can only be 
applied to a variable with static or thread storage duration" "" { target c++20 
} }
+    ;
+#endif
+  for (inline auto [ a ] : z)          // { dg-error "structured binding 
declaration cannot be 'inline'" }
+    ;                                  // { dg-warning "structured bindings 
only available with" "" { target c++14_down } .-1 }
+}

        Jakub

Reply via email to