Hi!

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.

Tested on x86_64-linux.

2026-03-26  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