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