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
