On Thu, Mar 26, 2026 at 12:57:48PM +0100, Jakub Jelinek wrote:
> The current behavior of GCC is that we list all annotations gathered from
> all the PARM_DECLs merged together on ^^fnparm or
> variable_of (parameters_of (^^fn)[0]) and throw on annotations_of
> on parameters_of (^^fn)[0].  The paper requires that the last one works
> like we currently handle the former two, and for the former two we
> filter out annotations that have not appeared on the function definition.
> 
> The following patch marks the annotations in grokfndecl before pushdecl
> etc. merges it with other decls.

Reposting as no longer RFC PATCH.
Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

2026-03-28  Jakub Jelinek  <[email protected]>

        * decl.cc (grokfndecl): For -freflection mark annotations of
        PARM_DECLs in a function definition.
        * reflect.cc (eval_annotations_of): Allow annotations_of on
        function parameters.  For r which is a PARM_DECL without REFLECT_PARM,
        filter out annotations not marked by grokfndecl.
        (reflection_mangle_prefix): Preserve the grokfndecl marking of
        annotations during mangling.

        * g++.dg/reflect/annotations15.C: New test.

--- gcc/cp/decl.cc.jj   2026-03-26 07:59:59.527656320 +0100
+++ gcc/cp/decl.cc      2026-03-26 12:31:36.887319793 +0100
@@ -12218,7 +12218,31 @@ grokfndecl (tree ctype,
 
   DECL_ARGUMENTS (decl) = parms;
   for (t = parms; t; t = DECL_CHAIN (t))
-    DECL_CONTEXT (t) = decl;
+    {
+      DECL_CONTEXT (t) = decl;
+      if (flag_reflection
+         && initialized == SD_INITIALIZED
+         && DECL_ATTRIBUTES (t))
+       for (tree a = DECL_ATTRIBUTES (t);
+            (a = lookup_attribute ("internal ", "annotation ", a));
+            a = TREE_CHAIN (a))
+         {
+           gcc_checking_assert (TREE_CODE (TREE_VALUE (a)) == TREE_LIST);
+           /* Mark TREE_PURPOSE of the value that it is an annotation
+              on an argument of a function definition (rather than
+              annotation from function declaration).  For function parameter
+              reflection all annotations are listed, while for variable_of
+              only those marked here.  Annotation is marked as coming from
+              function definition's argument if it has TREE_PURPOSE
+              void_cst or INTEGER_CST with signed type.  */
+           tree val = TREE_VALUE (a);
+           if (TREE_PURPOSE (val) == NULL_TREE)
+             TREE_PURPOSE (val) = void_node;
+           else if (TREE_CODE (TREE_PURPOSE (val)) == INTEGER_CST)
+             TREE_PURPOSE (val)
+               = fold_convert (sbitsizetype, TREE_PURPOSE (val));
+         }
+    }
 
   /* Propagate volatile out from type to decl.  */
   if (TYPE_VOLATILE (type))
--- gcc/cp/reflect.cc.jj        2026-03-25 20:32:38.262640083 +0100
+++ gcc/cp/reflect.cc   2026-03-26 12:30:48.440143055 +0100
@@ -3812,14 +3812,16 @@ eval_annotations_of (location_t loc, con
        || eval_is_type_alias (r) == boolean_true_node
        || eval_is_variable (r, kind) == boolean_true_node
        || eval_is_function (r) == boolean_true_node
+       || eval_is_function_parameter (r, kind) == boolean_true_node
        || eval_is_namespace (r) == boolean_true_node
        || eval_is_enumerator (r) == boolean_true_node
        || eval_is_base (r, kind) == boolean_true_node
        || eval_is_nonstatic_data_member (r) == boolean_true_node))
     return throw_exception (loc, ctx,
                            "reflection does not represent a type,"
-                           " type alias, variable, function, namespace,"
-                           " enumerator, direct base class relationship,"
+                           " type alias, variable, function, function"
+                           " parameter, namespace, enumerator,"
+                           " direct base class relationship,"
                            " or non-static data member",
                            fun, non_constant_p, jump_target);
 
@@ -3836,6 +3838,7 @@ eval_annotations_of (location_t loc, con
     }
 
   r = maybe_get_first_fn (r);
+  bool var_of = false;
   if (kind == REFLECT_BASE)
     {
       gcc_assert (TREE_CODE (r) == TREE_BINFO);
@@ -3860,7 +3863,11 @@ eval_annotations_of (location_t loc, con
        r = TYPE_ATTRIBUTES (r);
     }
   else if (DECL_P (r))
-    r = DECL_ATTRIBUTES (r);
+    {
+      if (TREE_CODE (r) == PARM_DECL && kind != REFLECT_PARM)
+       var_of = true;
+      r = DECL_ATTRIBUTES (r);
+    }
   else
     gcc_unreachable ();
   vec<constructor_elt, va_gc> *elts = nullptr;
@@ -3869,6 +3876,15 @@ eval_annotations_of (location_t loc, con
     {
       gcc_checking_assert (TREE_CODE (TREE_VALUE (a)) == TREE_LIST);
       tree val = TREE_VALUE (TREE_VALUE (a));
+      tree purpose = TREE_PURPOSE (TREE_VALUE (a));
+      if (var_of
+         && (purpose == NULL_TREE
+             || (TREE_CODE (purpose) == INTEGER_CST
+                 && !TYPE_UNSIGNED (TREE_TYPE (purpose)))))
+       /* For ^^fnparm or variable_of (parameters_of (^^fn)[N])
+          filter out annotations not specified on the function
+          definition.  */
+       continue;
       if (type)
        {
          tree at = TREE_TYPE (val);
@@ -8749,7 +8765,13 @@ reflection_mangle_prefix (tree refl, cha
       strcpy (prefix, "an");
       if (TREE_PURPOSE (TREE_VALUE (h)) == NULL_TREE)
        TREE_PURPOSE (TREE_VALUE (h))
-         = build_int_cst (integer_type_node, annotation_idx++);
+         = bitsize_int (annotation_idx++);
+      /* TREE_PURPOSE void_cst or INTEGER_CST with signed type
+        means it is annotation which should appear in
+        variable_of list.  */
+      else if (TREE_PURPOSE (TREE_VALUE (h)) == void_node)
+       TREE_PURPOSE (TREE_VALUE (h))
+         = sbitsize_int (annotation_idx++);
       return TREE_PURPOSE (TREE_VALUE (h));
     }
   if (eval_is_type_alias (h) == boolean_true_node)
--- gcc/testsuite/g++.dg/reflect/annotations15.C.jj     2026-03-26 
12:40:48.910963751 +0100
+++ gcc/testsuite/g++.dg/reflect/annotations15.C        2026-03-26 
12:40:37.661154305 +0100
@@ -0,0 +1,44 @@
+// P3795R2 - Miscellaneous Reflection Cleanup
+// { dg-do compile { target c++26 } }
+// { dg-additional-options "-freflection" }
+
+#include <meta>
+
+void foo ([[=1]] int x);
+constexpr auto rf = parameters_of (^^foo)[0];
+void foo ([[=2, =3]] int x);
+
+void
+foo ([[=4, =5]] int x)
+{
+  static_assert (annotations_of (^^x).size () == 2);
+  static_assert ([: constant_of (annotations_of (^^x)[0]) :] == 4);
+  static_assert ([: constant_of (annotations_of (^^x)[1]) :] == 5);
+  static_assert (annotations_of (rf).size () == 5);
+  static_assert ([: constant_of (annotations_of (rf)[0]) :] == 1);
+  static_assert ([: constant_of (annotations_of (rf)[1]) :] == 2);
+  static_assert ([: constant_of (annotations_of (rf)[2]) :] == 3);
+  static_assert ([: constant_of (annotations_of (rf)[3]) :] == 4);
+  static_assert ([: constant_of (annotations_of (rf)[4]) :] == 5);
+  static_assert (annotations_of (variable_of (rf)).size () == 2);
+  static_assert ([: constant_of (annotations_of (variable_of (rf))[0]) :] == 
4);
+  static_assert ([: constant_of (annotations_of (variable_of (rf))[1]) :] == 
5);
+  static_assert (annotations_of (parameters_of (^^foo)[0]).size () == 5);
+  static_assert ([: constant_of (annotations_of (parameters_of (^^foo)[0])[0]) 
:] == 1);
+  static_assert ([: constant_of (annotations_of (parameters_of (^^foo)[0])[1]) 
:] == 2);
+  static_assert ([: constant_of (annotations_of (parameters_of (^^foo)[0])[2]) 
:] == 3);
+  static_assert ([: constant_of (annotations_of (parameters_of (^^foo)[0])[3]) 
:] == 4);
+  static_assert ([: constant_of (annotations_of (parameters_of (^^foo)[0])[4]) 
:] == 5);
+  static_assert (annotations_of (^^x)[0] == annotations_of (rf)[3]);
+  static_assert (annotations_of (^^x)[0] == annotations_of (variable_of 
(rf))[0]);
+  static_assert (annotations_of (^^x)[0] == annotations_of (parameters_of 
(^^foo)[0])[3]);
+}
+
+void foo ([[=6]] int x);
+static_assert (annotations_of (parameters_of (^^foo)[0]).size () == 6);
+static_assert ([: constant_of (annotations_of (parameters_of (^^foo)[0])[0]) 
:] == 1);
+static_assert ([: constant_of (annotations_of (parameters_of (^^foo)[0])[1]) 
:] == 2);
+static_assert ([: constant_of (annotations_of (parameters_of (^^foo)[0])[2]) 
:] == 3);
+static_assert ([: constant_of (annotations_of (parameters_of (^^foo)[0])[3]) 
:] == 4);
+static_assert ([: constant_of (annotations_of (parameters_of (^^foo)[0])[4]) 
:] == 5);
+static_assert ([: constant_of (annotations_of (parameters_of (^^foo)[0])[5]) 
:] == 6);


        Jakub

Reply via email to