On Mon, Aug 25, 2025 at 09:44:29AM -0400, Jason Merrill wrote: > > --- 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); > > Rather than checking specifically for an expansion stmt, I'd prefer to check > for the general case of the parameter not being in a template, perhaps with > > if (!uses_template_parms (DECL_CONTEXT (t)) > > since that test is already used in the controlling if?
That works too on GXX_TESTSUITE_STDS=98,11,14,17,20,23,26 make check-g++ RUNTESTFLAGS="dg.exp='expansion-stmt*'" too, ok for trunk if it passes full bootstrap/regtest? 2025-08-25 Jakub Jelinek <ja...@redhat.com> Jason Merrill <ja...@redhat.com> PR c++/121575 * pt.cc (tsubst_expr) <case PARM_DECL>: If DECL_CONTEXT (t) isn't a template return t for PARM_DECLs without local specialization. * g++.dg/cpp26/expansion-stmt20.C: New test. --- gcc/cp/pt.cc.jj 2025-08-25 16:29:07.122358976 +0200 +++ gcc/cp/pt.cc 2025-08-25 16:45:15.210314704 +0200 @@ -22321,6 +22321,11 @@ tsubst_expr (tree t, tree args, tsubst_f if (DECL_NAME (t) == this_identifier && current_class_ptr) RETURN (current_class_ptr); + /* Parameters of non-templates map to themselves (e.g. in + expansion statement body). */ + if (!uses_template_parms (DECL_CONTEXT (t))) + 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-25 16:43:07.510020392 +0200 +++ gcc/testsuite/g++.dg/cpp26/expansion-stmt20.C 2025-08-25 16:43:07.510020392 +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