https://gcc.gnu.org/g:66455591fac1e80b5acc615598cbf556d565e080

commit r16-2046-g66455591fac1e80b5acc615598cbf556d565e080
Author: Jakub Jelinek <ja...@redhat.com>
Date:   Mon Jul 7 09:17:34 2025 +0200

    c++: Pedwarn on invalid decl specifiers for for-range-declaration [PR84009]
    
    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.
    
    2025-07-07  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.

Diff:
---
 gcc/cp/parser.cc                         | 30 ++++++++++++++++++++++-
 gcc/testsuite/g++.dg/cpp0x/range-for40.C | 41 +++++++++++++++++++++++++++++++
 gcc/testsuite/g++.dg/cpp0x/range-for41.C | 42 ++++++++++++++++++++++++++++++++
 gcc/testsuite/g++.dg/cpp0x/range-for42.C | 41 +++++++++++++++++++++++++++++++
 gcc/testsuite/g++.dg/cpp0x/range-for43.C | 42 ++++++++++++++++++++++++++++++++
 5 files changed, 195 insertions(+), 1 deletion(-)

diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index cac74e36ece9..44a78324c6e6 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -16919,6 +16919,15 @@ cp_parser_decomposition_declaration (cp_parser *parser,
       /* 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],
+                0, "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],
+                0, "for-range-declaration cannot be %qs",
+                "static");
     }
 
   if (pushed_scope)
@@ -24162,7 +24171,26 @@ cp_parser_init_declarator (cp_parser* parser,
          && 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],
+                        0, "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],
+                        0, "for-range-declaration cannot be %qs",
+                        "static");
+             else if (decl_specifiers->storage_class == sc_extern)
+               pedwarn (decl_specifiers->locations[ds_storage_class],
+                        0, "for-range-declaration cannot be %qs",
+                        "extern");
+             else if (decl_specifiers->storage_class == sc_register)
+               pedwarn (decl_specifiers->locations[ds_storage_class],
+                        0, "for-range-declaration cannot be %qs",
+                        "register");
+           }
          else
            {
              if (!maybe_range_for_decl)
diff --git a/gcc/testsuite/g++.dg/cpp0x/range-for40.C 
b/gcc/testsuite/g++.dg/cpp0x/range-for40.C
new file mode 100644
index 000000000000..dea4a2a1ba8f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/range-for40.C
@@ -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 }
+}
diff --git a/gcc/testsuite/g++.dg/cpp0x/range-for41.C 
b/gcc/testsuite/g++.dg/cpp0x/range-for41.C
new file mode 100644
index 000000000000..d69036545297
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/range-for41.C
@@ -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)              // { dg-warning "for-range-declaration 
cannot be 'static'" }
+    ;
+  for (thread_local auto a : z)                // { dg-warning 
"for-range-declaration cannot be 'thread_local'" }
+    ;
+  for (__thread auto a : z)            // { dg-warning "for-range-declaration 
cannot be '__thread'" }
+    ;                                  // { dg-warning "function-scope 'a' 
implicitly auto and declared '__thread'" "" { target *-*-* } .-1 }
+  for (register auto a : z)            // { dg-warning "for-range-declaration 
cannot be 'register'" }
+    ;                                  // { dg-warning "does not allow 
'register' storage class specifier" "" { target c++17 } .-1 }
+  for (extern auto a : z)              // { dg-warning "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 }
+}
diff --git a/gcc/testsuite/g++.dg/cpp0x/range-for42.C 
b/gcc/testsuite/g++.dg/cpp0x/range-for42.C
new file mode 100644
index 000000000000..a5d94fc43ef6
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/range-for42.C
@@ -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 }
+}
diff --git a/gcc/testsuite/g++.dg/cpp0x/range-for43.C 
b/gcc/testsuite/g++.dg/cpp0x/range-for43.C
new file mode 100644
index 000000000000..77060e3dcb98
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/range-for43.C
@@ -0,0 +1,42 @@
+// 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 "for-range-declaration 
cannot be 'static'" }
+    ;                                  // { dg-warning "structured binding 
declaration can be 'static' only in" "" { target c++17_down } .-1 }
+                                       // { dg-warning "structured bindings 
only available with" "" { target c++14_down } .-2 }
+  for (thread_local auto [ a ] : z)    // { dg-warning "for-range-declaration 
cannot be 'thread_local'" }
+    ;                                  // { dg-warning "structured binding 
declaration can be 'thread_local' only in" "" { target c++17_down } .-1 }
+                                       // { dg-warning "structured bindings 
only available with" "" { target c++14_down } .-2 }
+  for (__thread auto [ a ] : z)                // { dg-warning 
"for-range-declaration cannot be '__thread'" }
+    ;                                  // { dg-warning "function-scope 
'structured binding' implicitly auto and declared '__thread'" "" { target *-*-* 
} .-1 }
+                                       // { dg-warning "structured binding 
declaration can be '__thread' only in" "" { target c++17_down } .-2 }
+                                       // { dg-warning "structured bindings 
only available with" "" { target c++14_down } .-3 }
+  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 }
+}

Reply via email to