]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
c++: Pedwarn on invalid decl specifiers for for-range-declaration [PR84009]
authorJakub Jelinek <jakub@redhat.com>
Mon, 7 Jul 2025 07:17:34 +0000 (09:17 +0200)
committerJakub Jelinek <jakub@gcc.gnu.org>
Mon, 7 Jul 2025 07:25:15 +0000 (09:25 +0200)
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  <jakub@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
gcc/testsuite/g++.dg/cpp0x/range-for40.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp0x/range-for41.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp0x/range-for42.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp0x/range-for43.C [new file with mode: 0644]

index cac74e36ece92765b64fd16395ad4dd5b6afccc4..44a78324c6e66dc9b032868dace083b2d6bf977a 100644 (file)
@@ -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 (file)
index 0000000..dea4a2a
--- /dev/null
@@ -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 (file)
index 0000000..d690365
--- /dev/null
@@ -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 (file)
index 0000000..a5d94fc
--- /dev/null
@@ -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 (file)
index 0000000..77060e3
--- /dev/null
@@ -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 }
+}