Hi! The following testcase shows an ICE when a parameter of a non-template function is referenced in expansion stmt body.
tsubst_expr in that case assumes that either the PARM_DECL has registered local specialization, or is this argument or it is in unevaluated context. Parameters are always defined outside of the expansion statement for-range-declaration or body, so for the instantiation of the body outside of templates should always map to themselves. It could be fixed by registering local self-specializations for all the function parameters, but just handling it in tsubst_expr seems to be easier and less costly. Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk? 2025-08-25 Jakub Jelinek <ja...@redhat.com> PR c++/121575 * pt.cc (tsubst_expr) <case PARM_DECL>: During expansion stmt body instantiation outside of templates return t for PARM_DECLs without local specialization. * g++.dg/cpp26/expansion-stmt20.C: New test. --- gcc/cp/pt.cc.jj 2025-08-23 15:00:04.262787988 +0200 +++ gcc/cp/pt.cc 2025-08-23 15:51:08.726081054 +0200 @@ -22321,6 +22321,13 @@ tsubst_expr (tree t, tree args, tsubst_f if (DECL_NAME (t) == this_identifier && current_class_ptr) RETURN (current_class_ptr); + /* Parameters during expansion stmt body instantiation outside + of templates map to themselves. */ + if (current_tinst_level + && (TREE_CODE (current_tinst_level->tldcl) + == TEMPLATE_FOR_STMT)) + RETURN (t); + /* This can happen for a parameter name used later in a function declaration (such as in a late-specified return type). Just make a dummy decl, since it's only used for its type. */ --- gcc/testsuite/g++.dg/cpp26/expansion-stmt20.C.jj 2025-08-23 16:13:06.881488788 +0200 +++ gcc/testsuite/g++.dg/cpp26/expansion-stmt20.C 2025-08-23 15:58:11.641445497 +0200 @@ -0,0 +1,59 @@ +// PR c++/121575 +// { dg-do run { target c++11 } } +// { dg-options "" } + +struct A { int x, y; }; +int c; + +void +qux (A p) +{ + if (p.x != 1 || p.y != 3) + __builtin_abort (); + ++c; +} + +void +foo () +{ + A p { 1, 3 }; + template for (auto _ : {}) // { dg-warning "'template for' only available with" "" { target c++23_down } } + qux (p); + template for (auto _ : { 0 }) // { dg-warning "'template for' only available with" "" { target c++23_down } } + qux (p); +} + +void +bar (A p) +{ + template for (auto _ : {}) // { dg-warning "'template for' only available with" "" { target c++23_down } } + qux (p); + template for (auto _ : { 0, 1 }) // { dg-warning "'template for' only available with" "" { target c++23_down } } + qux (p); +} + +A +baz () +{ + A p { 1, 3 }; + template for (auto _ : {}) // { dg-warning "'template for' only available with" "" { target c++23_down } } + qux (p); + template for (auto _ : { 0, 1, 2 }) // { dg-warning "'template for' only available with" "" { target c++23_down } } + qux (p); + return p; +} + +int +main () +{ + foo (); + if (c != 1) + __builtin_abort (); + bar ({ 1, 3 }); + if (c != 3) + __builtin_abort (); + if (baz ().x != 1 || baz ().y != 3) + __builtin_abort (); + if (c != 9) + __builtin_abort (); +} Jakub