c++: Implement CWG3119 - for-range-declaration of an expansion-statement as a templated entity The following patch implements the proposed resolution of https://wg21.link/cwg3119 Temporarily setting processing_template_decl around the cp_parser_simple_declaration causes all kinds of ICEs and miscompilations, this patch just sets in_expansion_stmt around it and allows the sb pack in that case. 2026-04-04 Jakub Jelinek <jakub@redhat.com> * parser.cc (cp_parser_expansion_statement): Temporarily set in_expansion_stmt to true around cp_parser_simple_declaration for range_decl. (cp_parser_decomposition_declaration): Don't reject structured binding packs if in_expansion_stmt is set. * g++.dg/cpp26/expansion-stmt34.C: New test.
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc index 84eef39..518858f 100644 --- a/gcc/cp/parser.cc +++ b/gcc/cp/parser.cc
@@ -16606,6 +16606,8 @@ /* A colon is used in expansion-statement. */ parser->colon_corrects_to_scope_p = false; + in_expansion_stmt = true; + /* Parse the declaration. */ tree range_decl; cp_parser_simple_declaration (parser, @@ -16614,6 +16616,7 @@ if (range_decl == NULL_TREE) range_decl = error_mark_node; parser->colon_corrects_to_scope_p = saved_colon_corrects_to_scope_p; + in_expansion_stmt = save_in_expansion_stmt; cp_parser_require (parser, CPP_COLON, RT_COLON); @@ -18254,7 +18257,7 @@ if (cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS)) { location_t elloc = cp_lexer_peek_token (parser->lexer)->location; - if (!processing_template_decl) + if (!processing_template_decl && !in_expansion_stmt) error_at (elloc, "structured binding pack outside of template"); else if (pack != -1) error_at (elloc,
diff --git a/gcc/testsuite/g++.dg/cpp26/expansion-stmt34.C b/gcc/testsuite/g++.dg/cpp26/expansion-stmt34.C new file mode 100644 index 0000000..b35f022 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp26/expansion-stmt34.C
@@ -0,0 +1,40 @@ +// CWG3119 - for-range-declaration of an expansion-statement as a templated +// entity. +// { dg-do run { target c++26 } } + +namespace std { + using size_t = decltype (sizeof 0); + template<typename T> struct tuple_size; + template<size_t, typename> struct tuple_element; +} +struct B { int a; long b; }; +struct C { char a; short b; int c; long d; long long e; float f; }; +struct E { char a; template <int I> char &get () { return a; } }; + +template <> struct std::tuple_size <E> { static const int value = 7; }; +template <std::size_t I> struct std::tuple_element <I, E> { using type = char; }; + +struct A +{ + B b; + C c; + int d[3]; + E e; +}; + +template <typename ...T> +int +foo (T... x) +{ + return sizeof... (x); +} + +int +main () +{ + int ret = 0; + template for (auto [...e] : A ()) + ret += foo (e...); + if (ret != 18) + __builtin_abort (); +}