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

Reply via email to