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.

> ---
>  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.

> +      resolve_type_of_reflected_decl (t);
>        /* Avoid -Wunused-but-set* warnings when a variable or parameter
>        is just set and reflected.  */
>        if (VAR_P (t) || TREE_CODE (t) == PARM_DECL)
> @@ -2533,6 +2545,7 @@ has_type (tree r, reflect_kind kind)
>      {
>        if (DECL_CONSTRUCTOR_P (r) || DECL_DESTRUCTOR_P (r))
>       return false;
> +      resolve_type_of_reflected_decl (r);
>        if (undeduced_auto_decl (r))
>       return false;
>        return true;
> @@ -5532,7 +5545,12 @@ eval_can_substitute (location_t loc, const 
> constexpr_ctx *ctx,
>        if (fn == error_mark_node)
>       return boolean_false_node;
>        fn = resolve_nondeduced_context_or_error (fn, tf_none);
> -      if (fn == error_mark_node || undeduced_auto_decl (fn))
> +      if (fn == error_mark_node)
> +     return boolean_false_node;
> +      if (BASELINK_P (fn))
> +     fn = BASELINK_FUNCTIONS (fn);

This can be simplified to MAYBE_BASELINK_FUNCTIONS.

> +      resolve_type_of_reflected_decl (fn);
> +      if (undeduced_auto_decl (fn))
>       return boolean_false_node;
>        return boolean_true_node;
>      }
> @@ -6759,8 +6777,12 @@ members_of_representable_p (tree c, tree r)
>         || TREE_CODE (r) == FIELD_DECL
>         || TREE_CODE (r) == NAMESPACE_DECL)
>       return true;
> -      if (VAR_OR_FUNCTION_DECL_P (r) && !undeduced_auto_decl (r))
> -     return true;
> +      if (VAR_OR_FUNCTION_DECL_P (r))
> +     {
> +       resolve_type_of_reflected_decl (r);
> +       if (!undeduced_auto_decl (r))
> +         return true;
> +     }
>      }
>    return false;
>  }
> diff --git a/gcc/testsuite/g++.dg/reflect/can_substitute2.C 
> b/gcc/testsuite/g++.dg/reflect/can_substitute2.C
> new file mode 100644
> index 000000000000..6628a1b72060
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/reflect/can_substitute2.C
> @@ -0,0 +1,19 @@
> +// [meta.reflection.substitute] Example 1
> +// { dg-do compile { target c++26 } }
> +// { dg-additional-options "-freflection" }
> +
> +#include <meta>
> +
> +template<class T>
> +auto fn1();
> +
> +static_assert(!can_substitute(^^fn1, {^^int}));
> +constexpr auto r1 = substitute(^^fn1, {^^int}); // { dg-error 
> "can_substitute returned false" }
> +
> +template<class T>
> +auto fn2() {
> +  static_assert(false); // { dg-error "assert" }
> +  return T{};
> +}
> +
> +constexpr bool r2 = can_substitute(^^fn2, {^^int}); // { dg-message 
> "required from here" }
> diff --git a/gcc/testsuite/g++.dg/reflect/members_of14.C 
> b/gcc/testsuite/g++.dg/reflect/members_of14.C
> new file mode 100644
> index 000000000000..6a969063aa4b
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/reflect/members_of14.C
> @@ -0,0 +1,29 @@
> +// PR c++/124628
> +// { dg-do compile { target c++26 } }
> +// { dg-additional-options "-freflection" }
> +
> +#include <meta>
> +
> +template<class T> void f();
> +
> +template<class T>
> +struct A {
> +  void g() noexcept(noexcept(T{}));
> +  auto h() { return T{}; }
> +  static inline auto m = T{};
> +  // A& operator=(const A&) noexcept;
> +  // A& operator=(A&&) noexcept;
> +};
> +
> +int main() {
> +  constexpr auto ac = std::meta::access_context::current();
> +  template for (constexpr auto mem : 
> define_static_array(members_of(^^A<int>, ac)))
> +    if constexpr (!is_constructor(mem) && !is_destructor(mem))
> +      f<typename [:type_of(mem):]>();
> +}
> +
> +// { dg-final { scan-assembler _Z1fIDoFvvEEvv } } void f<void () noexcept>()
> +// { dg-final { scan-assembler _Z1fIFivEEvv } } void f<int ()>()
> +// { dg-final { scan-assembler _Z1fIiEvv } } void f<int>()
> +// { dg-final { scan-assembler _Z1fIDoFR1AIiERKS1_EEvv } } void f<A<int>& 
> (A<int> const&) noexcept>()
> +// { dg-final { scan-assembler _Z1fIDoFR1AIiEOS1_EEvv } } void f<A<int>& 
> (A<int>&&) noexcept>()
> diff --git a/gcc/testsuite/g++.dg/reflect/substitute3.C 
> b/gcc/testsuite/g++.dg/reflect/substitute3.C
> index ff1b1aeaf4da..19329a80efea 100644
> --- a/gcc/testsuite/g++.dg/reflect/substitute3.C
> +++ b/gcc/testsuite/g++.dg/reflect/substitute3.C
> @@ -7,9 +7,7 @@
>  
>  template<typename>
>  auto
> -f ()
> -{
> -}
> +f ();
>  
>  consteval bool
>  g ()
> diff --git a/gcc/testsuite/g++.dg/reflect/type_of3.C 
> b/gcc/testsuite/g++.dg/reflect/type_of3.C
> index ba28c4f59808..b739851da2e0 100644
> --- a/gcc/testsuite/g++.dg/reflect/type_of3.C
> +++ b/gcc/testsuite/g++.dg/reflect/type_of3.C
> @@ -10,4 +10,10 @@ struct S {
>  };
>  int h() { return 0; }
>  
> +template<class T>
> +struct ST {
> +    auto g() { return T{}; }
> +};
> +
>  static_assert(type_of(^^S::g) == type_of(^^h));
> +static_assert(type_of(^^ST<int>::g) == type_of(^^h));
> -- 
> 2.54.0.rc1.54.g60f07c4f5c
> 

Marek

Reply via email to