On Thu, May 14, 2026 at 01:47:16PM -0400, Patrick Palka wrote:
> On Thu, 14 May 2026, Marek Polacek wrote:
>
> > On Thu, May 14, 2026 at 12:28:36PM -0400, Patrick Palka wrote:
> > > Tested on x86_64-pc-linux-gnu, does this look OK for trunk/16?
> > >
> > > -- >8 --
> > >
> > > Various reflection queries reject functions (or variables) with an
> > > undeduced return type. But this assumes return type deduction has
> > > already been attempted which is not the case if the function is a
> > > specialization that has not yet been ODR-used or otherwise instantiated.
> > > Similarly we can also have an uninstantiated noexcept which we should
> > > also instantiate at this point.
> > >
> > > Rather an inventing a new way to resolve the type of such a function
> > > or variable for reflection purposes, I think we can just silently call
> > > mark_used in an unevaluated context, making it behave similarly to
> > > requires { &decl; }. Since diagnostics (in the immediate context) will
> > > be suppressed it means we'll gracefully handle deleted functions or
> > > those with unsatisfied constraints, leaving it up to the caller to
> > > handle them.
> > >
> > > PR c++/124628
> > >
> > > gcc/cp/ChangeLog:
> > >
> > > * reflect.cc (resolve_type_of_reflected_decl): New.
> > > (get_reflection): Call resolve_type_of_reflected_decl instead
> > > of mark_used.
> > > (has_type): Call resolve_type_of_reflected_decl before
> > > checking for an undeduced auto.
> > > (eval_can_substitute): Likewise. Also look through BASELINK.
> > > (members_of_representable): Call resolve_type_of_reflected_decl
> > > before checking for an undeduced auto.
> > >
> > > gcc/testsuite/ChangeLog:
> > >
> > > * g++.dg/reflect/can_substitute2.C: New test.
> > > * g++.dg/reflect/members_of14.C: New test.
> > > * g++.dg/reflect/substitute3.C: Adjust test so that f<int>'s
> > > return type fails to get deduced.
> > > * g++.dg/reflect/type_of3.C: Extend test to check for
> >
> > Something seems to be missing here.
>
> Oops, will fix.
>
> >
> > > ---
> > > gcc/cp/reflect.cc | 32 ++++++++++++++++---
> > > .../g++.dg/reflect/can_substitute2.C | 19 +++++++++++
> > > gcc/testsuite/g++.dg/reflect/members_of14.C | 29 +++++++++++++++++
> > > gcc/testsuite/g++.dg/reflect/substitute3.C | 4 +--
> > > gcc/testsuite/g++.dg/reflect/type_of3.C | 6 ++++
> > > 5 files changed, 82 insertions(+), 8 deletions(-)
> > > create mode 100644 gcc/testsuite/g++.dg/reflect/can_substitute2.C
> > > create mode 100644 gcc/testsuite/g++.dg/reflect/members_of14.C
> > >
> > > diff --git a/gcc/cp/reflect.cc b/gcc/cp/reflect.cc
> > > index ad4c77fab3eb..96450a7ebb58 100644
> > > --- a/gcc/cp/reflect.cc
> > > +++ b/gcc/cp/reflect.cc
> > > @@ -74,6 +74,19 @@ init_reflection ()
> > > pop_namespace ();
> > > }
> > >
> > > +/* Ensure the type of DECL is fully resolved by performing return
> > > + type deduction and deferred noexcept instantiation. */
> > > +
> > > +static void
> > > +resolve_type_of_reflected_decl (tree decl)
> > > +{
> > > + cp_unevaluated u;
> > > + /* Quietly calling mark_used in an unevaluated context will perform
> > > + all necessary checks and instantiations while suppressing constraint
> > > + unsatisfaction and deletedness diagnostics. */
> > > + mark_used (decl, tf_none);
> >
> > Are you sure we can ignore the result of mark_used here? E.g. here...
> >
> > > +}
> > > +
> > > /* Create a REFLECT_EXPR expression of kind KIND around T. */
> > >
> > > static tree
> > > @@ -210,8 +223,7 @@ get_reflection (location_t loc, tree t, reflect_kind
> > > kind/*=REFLECT_UNDEF*/)
> > > t = resolve_nondeduced_context_or_error (t, tf_warning_or_error);
> > > /* The argument could have a deduced return type, so we need to
> > > instantiate it now to find out its type. */
> > > - if (!mark_used (t))
> > > - return error_mark_node;
> >
> > ... we'd return but now we don't. I would think
> > resolve_type_of_reflected_decl
> > should return what mark_used returned.
>
> The problem is that mark_used returns false when the function cannot be
> ODR-used, not only when its type is unresolved. In particular it
> returns false for a deleted function, but a deleted function is not a
> problem for most callers, in which case the result of mark_used is
> potentially misleading and checking it is at best an optimization. A
> 'true' result is great and means the function's type is fully resolved,
> but a 'false' result means either the function's type is not fully
> resolved, or it's deleted, or its constraints are unsatisfied, etc, so
> the caller must do further checks anyway.
>
> For the get_reflection call site in particular, the current mark_used
> check means we reject:
>
> template<class T> void g() = delete;
> constexpr auto r = ^^g<int>; // currently error: use of deleted function
>
> We need to ignore the result of mark_used in order to accept this. This
> is part of the unresolved CWG3166 so maybe this will end up being invalid,
> but for now it seems like something we should try to support, given that
> we already support (perhaps accidentally)
>
> void f() = delete;
> ^^f;
>
> since this doesn't take the mark_used code path in get_reflection.
Fair enough, thanks for the explanation. Patch LGTM (but you
already got an approval).
Marek