On Tue, Feb 17, 2026 at 07:37:00PM -0500, Patrick Palka wrote:
> 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.
Yes, thanks. eval_substitute could also add it, though I'm not
sure if that covers everything.
Tested dg.exp=reflect/* 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.
We can add the missing OVERLOAD in eval_substitute.
PR c++/124150
gcc/cp/ChangeLog:
* reflect.cc (eval_substitute): Add an OVERLOAD around
a DECL_FUNCTION_TEMPLATE_P.
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, 73 insertions(+), 1 deletion(-)
create mode 100644 gcc/testsuite/g++.dg/reflect/substitute4.C
diff --git a/gcc/cp/reflect.cc b/gcc/cp/reflect.cc
index 7c9e1815317..e64e5df58eb 100644
--- a/gcc/cp/reflect.cc
+++ b/gcc/cp/reflect.cc
@@ -5394,7 +5394,11 @@ eval_substitute (location_t loc, const constexpr_ctx
*ctx,
ret = finish_template_variable (ret, tf_none);
}
else
- ret = lookup_template_function (r, rvec);
+ {
+ if (DECL_FUNCTION_TEMPLATE_P (r))
+ r = ovl_make (r, NULL_TREE);
+ ret = lookup_template_function (r, rvec);
+ }
return get_reflection_raw (loc, ret);
}
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: 8ba1e2077f2827e7c3528b28c98d69606e284d1b
--
2.53.0