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 ();
+}