c++, v3: Implement the annotations_of on parms part of P3795R1

Message ID adNQMG0418k3_Xpn@tucnak
State New
Headers
Series c++, v3: Implement the annotations_of on parms part of P3795R1 |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_gcc_build--master-arm success Build passed
linaro-tcwg-bot/tcwg_gcc_build--master-aarch64 success Build passed
linaro-tcwg-bot/tcwg_gcc_check--master-arm success Test passed
linaro-tcwg-bot/tcwg_gcc_check--master-aarch64 success Test passed

Commit Message

Jakub Jelinek April 6, 2026, 6:18 a.m. UTC
  On Fri, Apr 03, 2026 at 04:37:20PM -0400, Jason Merrill wrote:
> "void_node"

Changed.

> > +	    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)
> 
> It seems impossible to have INTEGER_CST this early, we can't have tried to
> mangle a function we haven't even built yet.  And indeed the testcase
> doesn't exercise this case.  Maybe assert NULL_TREE instead?

Assert added instead.

> > +	/* For ^^fnparm or variable_of (parameters_of (^^fn)[N])
> > +	   filter out annotations not specified on the function
> > +	   definition.  */
> 
> Let's also mention that purpose is set in grokfndecl or
> reflection_mangle_prefix.

Done.

> Also "void_node".

Changed.

Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

2026-04-06  Jakub Jelinek  <jakub@redhat.com>

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



	Jakub
  

Comments

Jason Merrill April 6, 2026, 4:36 p.m. UTC | #1
On 4/6/26 2:18 AM, Jakub Jelinek wrote:
> On Fri, Apr 03, 2026 at 04:37:20PM -0400, Jason Merrill wrote:
>> "void_node"
> 
> Changed.
> 
>>> +	    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)
>>
>> It seems impossible to have INTEGER_CST this early, we can't have tried to
>> mangle a function we haven't even built yet.  And indeed the testcase
>> doesn't exercise this case.  Maybe assert NULL_TREE instead?
> 
> Assert added instead.
> 
>>> +	/* For ^^fnparm or variable_of (parameters_of (^^fn)[N])
>>> +	   filter out annotations not specified on the function
>>> +	   definition.  */
>>
>> Let's also mention that purpose is set in grokfndecl or
>> reflection_mangle_prefix.
> 
> Done.
> 
>> Also "void_node".
> 
> Changed.
> 
> Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

OK.

> 2026-04-06  Jakub Jelinek  <jakub@redhat.com>
> 
> 	* 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,28 @@ 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_node or INTEGER_CST with signed type.  */
> +	    tree val = TREE_VALUE (a);
> +	    gcc_assert (TREE_PURPOSE (val) == NULL_TREE);
> +	    TREE_PURPOSE (val) = void_node;
> +	  }
> +    }
>   
>     /* 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,16 @@ 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.  TREE_PURPOSE is set in grokfndecl and/or in
> +	   reflection_mangle_prefix.  */
> +	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_node 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
>
  

Patch

--- 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,28 @@  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_node or INTEGER_CST with signed type.  */
+	    tree val = TREE_VALUE (a);
+	    gcc_assert (TREE_PURPOSE (val) == NULL_TREE);
+	    TREE_PURPOSE (val) = void_node;
+	  }
+    }
 
   /* 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,16 @@  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.  TREE_PURPOSE is set in grokfndecl and/or in
+	   reflection_mangle_prefix.  */
+	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_node 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);