un Tue, 17 Feb 2026, Marek Polacek wrote:
> Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?
>
> -- >8 --
> A function template is supposed to be wrapped in an OVERLOAD. Since
> in certain cases like members_of it is not, splice makes sure to add
> the OVERLOAD if needed. But it wasn't looking into TEMPLATE_ID_EXPRs
> and so we ended up with a "naked" TEMPLATE_DECL and crashed.
>
> PR c++/124150
>
> gcc/cp/ChangeLog:
>
> * reflect.cc (splice): When adding an OVERLOAD around
> a DECL_FUNCTION_TEMPLATE_P, also look into TEMPLATE_ID_EXPR.
>
> gcc/testsuite/ChangeLog:
>
> * g++.dg/reflect/substitute4.C: New test.
> ---
> gcc/cp/reflect.cc | 6 +-
> gcc/testsuite/g++.dg/reflect/substitute4.C | 68 ++++++++++++++++++++++
> 2 files changed, 72 insertions(+), 2 deletions(-)
> create mode 100644 gcc/testsuite/g++.dg/reflect/substitute4.C
>
> diff --git a/gcc/cp/reflect.cc b/gcc/cp/reflect.cc
> index 7c9e1815317..cec9bf60a78 100644
> --- a/gcc/cp/reflect.cc
> +++ b/gcc/cp/reflect.cc
> @@ -8026,8 +8026,10 @@ splice (tree refl)
> and a lot of places assume that. Furthermore, if reflection comes
> from ^^fntmpl, it is wrapped with OVERLOAD already, only when
> it comes from e.g. members_of it is not. */
> - if (DECL_FUNCTION_TEMPLATE_P (refl))
> - refl = ovl_make (refl, NULL_TREE);
> + tree &op = (TREE_CODE (refl) == TEMPLATE_ID_EXPR
> + ? TREE_OPERAND (refl, 0) : refl);
> + if (DECL_FUNCTION_TEMPLATE_P (op))
> + op = ovl_make (op, NULL_TREE);
Is it possible to wrap it in OVERLOAD when the TEMPLATE_ID_EXPR is
initially built? That'd be preferable to in-place mutating it
after the fact.
>
> return refl;
> }
> diff --git a/gcc/testsuite/g++.dg/reflect/substitute4.C
> b/gcc/testsuite/g++.dg/reflect/substitute4.C
> new file mode 100644
> index 00000000000..ff9cd403e4b
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/reflect/substitute4.C
> @@ -0,0 +1,68 @@
> +// PR c++/124150
> +// { dg-do compile { target c++26 } }
> +// { dg-additional-options "-freflection" }
> +
> +#include <meta>
> +using namespace std;
> +
> +template <typename T>
> +consteval meta::info find_non_static_template_function() {
> + for (auto r : meta::members_of(^^T, meta::access_context::current())) {
> + if (meta::identifier_of(r) == "non_static_function") {
> + return r;
> + }
> + }
> + return ^^void;
> +}
> +template <typename T>
> +consteval meta::info find_static_template_member_func() {
> + for (auto r : meta::members_of(^^T, meta::access_context::current())) {
> + if (meta::identifier_of(r) == "static_function") {
> + return r;
> + }
> + }
> + return ^^void;
> +}
> +template <typename T>
> +consteval meta::info find_non_template_member_func() {
> + for (auto r : meta::members_of(^^T, meta::access_context::current())) {
> + if (meta::identifier_of(r) == "non_template_member_func") {
> + return r;
> + }
> + }
> + return ^^void;
> +}
> +
> +template <typename T, typename Ts, meta::info static_func, meta::info
> non_static_func,
> + meta::info non_template_func>
> +void call_member_template_functions(Ts* ptr) {
> + [:substitute(static_func, {^^T}):]();
> + ptr->[:substitute(non_static_func, {^^T}):]();
> + ptr->[:non_template_func:]();
> +}
> +
> +struct A {
> + template <typename T>
> + static void static_function() { }
> + template <typename T>
> + void non_static_function() { }
> + void non_template_member_func() { }
> +};
> +
> +template <typename T, meta::info r>
> +void call_non_member_template_function() {
> + [:meta::substitute(r, {^^T}):]();
> +}
> +
> +template <typename T>
> +void non_member_template_function() { }
> +
> +int
> +main ()
> +{
> + A a;
> + call_member_template_functions<int, A,
> find_static_template_member_func<A>(),
> + find_non_static_template_function<A>(),
> + find_non_template_member_func<A>()>(&a);
> + call_non_member_template_function<int, ^^non_member_template_function>();
> +}
>
> base-commit: 58b6004e920a0ddee5726b98e272405d112a8ec2
> --
> 2.53.0
>
>