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
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
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
>
@@ -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))
@@ -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)
@@ -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);