On Wed, Mar 25, 2026 at 09:06:31PM +0100, Jakub Jelinek wrote:
On Wed, Mar 25, 2026 at 05:50:40PM +0100, Jakub Jelinek wrote:
The following patch attempts to implement another part of P3795R1
(though with some changes Barry has queued for P3795R2, in particular
throwing on !has-type(r) in the annotations sequence or when the
annotations are non-empty and name is not specified), in particular
the addition of annotations to data_member_options and handling it in
data_member_spec/define_aggregate etc.
This is on top of the earlier data_member_spec patch, though even if
it is decided to revert that part of changes, it shouldn't be hard
to adjust this patch to apply.
Here is an updated patch which is not on top of the earlier data_member_spec
patch. And updates various function comments to include ANN etc.
Now reposted as no longer RFC PATCH given that it has been voted in and
properly successfully bootstrapped/regtested on x86_64-linux and i686-linux.
2026-03-28 Jakub Jelinek <[email protected]>
libstdc++-v3/
* include/std/meta (std::meta::data_member_options): Add annotations
member.
gcc/cp/
* reflect.cc (get_range_elts): If N is negative, just use the tree
as object to extract range from instead of finding Nth argument
of a call.
(eval_is_bit_field, eval_type_of, eval_size_of, eval_alignment_of,
eval_bit_size_of, eval_has_identifier, eval_identifier_of): Adjust
function comments from P3795R2.
(eval_display_string_of): Handle annotations in
REFLECT_DATA_MEMBER_SPEC.
(eval_annotations_of): Adjust function comments from P3795R2.
(eval_data_member_spec): Likewise. Read and diagnose annotations.
(eval_define_aggregate): Adjust function comments from P3795R2.
Create annotations.
(compare_reflections): Compare REFLECT_DATA_MEMBER_SPEC annotations.
* mangle.cc (write_reflection): Mangle REFLECT_DATA_MEMBER_SPEC
annotations.
gcc/testsuite/
* g++.dg/reflect/data_member_spec5.C: New test.
* g++.dg/reflect/data_member_spec6.C: New test.
* g++.dg/reflect/display_string_of1.C: Expect extra ", {}" before
")" for empty annotations, otherwise a list of annotations.
* g++.dg/reflect/u8display_string_of1.C: Likewise.
* g++.dg/reflect/define_aggregate9.C: New test.
* g++.dg/reflect/mangle1.C: Test mangling of REFLECT_DATA_MEMBER_SPEC
annotations.
--- libstdc++-v3/include/std/meta.jj 2026-03-25 20:19:10.759162427 +0100
+++ libstdc++-v3/include/std/meta 2026-03-25 20:19:53.791992428 +0100
@@ -422,6 +422,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
optional<int> alignment;
optional<int> bit_width;
bool no_unique_address = false;
+ vector<info> annotations;
};
consteval info data_member_spec(info, data_member_options);
consteval bool is_data_member_spec(info);
--- gcc/cp/reflect.cc.jj 2026-03-25 20:19:10.715163161 +0100
+++ gcc/cp/reflect.cc 2026-03-25 20:32:38.262640083 +0100
@@ -383,18 +383,25 @@ enum get_range_elts_kind {
eval_reflect_constant_array. For GET_INFO_VEC kind, <meta> ensures
the argument is reference to reflection_range concept and so both
range_value_t is info and range_refernce_t is cv info or cv info & or
- cv info &&. */
+ cv info &&. If N is negative, CALL is the expression to extract
+ values from rather than N-th argument from CALL. */
static tree
get_range_elts (location_t loc, const constexpr_ctx *ctx, tree call, int n,
bool *non_constant_p, bool *overflow_p, tree *jump_target,
get_range_elts_kind kind, tree fun)
{
- gcc_checking_assert (call_expr_nargs (call) > n);
- tree arg = get_nth_callarg (call, n);
- tree parm = DECL_ARGUMENTS (cp_get_callee_fndecl_nofold (call));
- for (int i = 0; i < n; ++i)
- parm = DECL_CHAIN (parm);
+ tree arg, parm;
+ if (n < 0)
+ arg = parm = call;
+ else
+ {
+ gcc_checking_assert (call_expr_nargs (call) > n);
+ arg = get_nth_callarg (call, n);
+ parm = DECL_ARGUMENTS (cp_get_callee_fndecl_nofold (call));
+ for (int i = 0; i < n; ++i)
+ parm = DECL_CHAIN (parm);
+ }
tree type = TREE_TYPE (arg);
gcc_checking_assert (TYPE_REF_P (type));
arg = cxx_eval_constant_expression (ctx, arg, vc_prvalue, non_constant_p,
@@ -539,7 +546,8 @@ get_range_elts (location_t loc, const co
tree call = finish_call_expr (obj, &args, true, false, complain);
if (call == error_mark_node)
return call;
- cp_walk_tree (&call, replace_parm_r, map, NULL);
+ if (n >= 0)
+ cp_walk_tree (&call, replace_parm_r, map, NULL);
if (complain != tf_none)
return call;
call = cxx_eval_constant_expression (ctx, call, vc_prvalue,
non_constant_p,
@@ -1853,7 +1861,7 @@ eval_is_explicit (tree r)
/* Process std::meta::is_bit_field.
Returns: true if r represents a bit-field, or if r represents a data member
- description (T,N,A,W,NUA) for which W is not _|_. Otherwise, false. */
+ description (T,N,A,W,NUA,ANN) for which W is not _|_. Otherwise, false. */
static tree
eval_is_bit_field (const_tree r, reflect_kind kind)
@@ -2518,8 +2526,8 @@ type_of (tree r, reflect_kind kind)
of the enum-specifier as specified in [dcl.enum].
-- Otherwise, if r represents a direct base class relationship (D,B), then
a reflection of B.
- -- Otherwise, for a data member description (T,N,A,W,NUA), a reflection of
- the type T. */
+ -- Otherwise, for a data member description (T,N,A,W,NUA,ANN), a reflection
+ of the type T. */
static tree
eval_type_of (location_t loc, const constexpr_ctx *ctx, tree r,
@@ -3169,7 +3177,7 @@ eval_offset_of (location_t loc, const co
/* Process std::meta::size_of.
Returns: If r represents
-- a non-static data member of type T,
- -- a data member description (T,N,A,W,NUA), or
+ -- a data member description (T,N,A,W,NUA,ANN), or
-- dealias(r) represents a type T,
then sizeof(T) if T is not a reference type and size_of(add_pointer(^^T))
otherwise. Otherwise, size_of(type_of(r)).
@@ -3178,7 +3186,7 @@ eval_offset_of (location_t loc, const co
-- dealias(r) is a reflection of a type, object, value, variable of
non-reference type, non-static data member that is not a bit-field,
direct base class relationship, or data member description
- (T,N,A,W,NUA) where W is not _|_.
+ (T,N,A,W,NUA,ANN) where W is not _|_.
-- If dealias(r) represents a type, then is_complete_type(r) is true. */
static tree
@@ -3229,12 +3237,13 @@ eval_size_of (location_t loc, const cons
-- Otherwise, if r represents a non-static data member M of a class C,
then the alignment of the direct member subobject corresponding to M of
a
complete object of type C.
- -- Otherwise, r represents a data member description (T,N,A,W,NUA).
+ -- Otherwise, r represents a data member description (T,N,A,W,NUA,ANN).
If A is not _|_, then the value A. Otherwise, alignment_of(^^T).
Throws: meta::exception unless all of the following conditions are met:
-- dealias(r) is a reflection of a type, object, variable of non-reference
type, non-static data member that is not a bit-field, direct base class
- relationship, or data member description (T,N,A,W,NUA) where W is _|_.
+ relationship, or data member description (T,N,A,W,NUA,ANN) where W is
+ _|_.
-- If dealias(r) represents a type, then is_complete_type(r) is true. */
static tree
@@ -3303,7 +3312,7 @@ eval_alignment_of (location_t loc, const
Returns:
-- If r represents an unnamed bit-field or a non-static data member that
is a bit-field with width W, then W.
- -- Otherwise, if r represents a data member description (T,N,A,W,NUA)
+ -- Otherwise, if r represents a data member description (T,N,A,W,NUA,ANN)
and W is not _|_, then W.
-- Otherwise, CHAR_BIT * size_of(r).
@@ -3400,8 +3409,8 @@ eval_bit_size_of (location_t loc, const
namespace, or namespace alias, then true.
-- Otherwise, if r represents a direct base class relationship, then
has_identifier(type_of(r)).
- -- Otherwise, r represents a data member description (T,N,A,W,NUA); true if
- N is not _|_. Otherwise, false. */
+ -- Otherwise, r represents a data member description (T,N,A,W,NUA,ANN);
+ true if N is not _|_. Otherwise, false. */
static tree
eval_has_identifier (tree r, reflect_kind kind)
@@ -3516,7 +3525,7 @@ eval_has_identifier (tree r, reflect_kin
the declaration of that entity.
-- Otherwise, if r represents a direct base class relationship, then
identifier_of(type_of(r)) or u8identifier_of(type_of(r)), respectively.
- -- Otherwise, r represents a data member description (T,N,A,W,NUA);
+ -- Otherwise, r represents a data member description (T,N,A,W,NUA,ANN);
a string_view or u8string_view, respectively, containing the identifier
N.
Throws: meta::exception unless has_identifier(r) is true and the identifier
@@ -3643,10 +3652,16 @@ eval_display_string_of (location_t loc,
pp_printf (&pp, "%T: %T", d, BINFO_TYPE (r));
}
else if (kind == REFLECT_DATA_MEMBER_SPEC)
- pp_printf (&pp, "(%T, %E, %E, %E, %s)", TREE_VEC_ELT (r, 0),
- TREE_VEC_ELT (r, 1), TREE_VEC_ELT (r, 2), TREE_VEC_ELT (r, 3),
- TREE_VEC_ELT (r, 4) == boolean_true_node
- ? "true" : "false");
+ {
+ pp_printf (&pp, "(%T, %E, %E, %E, %s, {", TREE_VEC_ELT (r, 0),
+ TREE_VEC_ELT (r, 1), TREE_VEC_ELT (r, 2), TREE_VEC_ELT (r, 3),
+ TREE_VEC_ELT (r, 4) == boolean_true_node
+ ? "true" : "false");
+ for (int i = 5; i < TREE_VEC_LENGTH (r); ++i)
+ pp_printf (&pp, "%s%E", i == 5 ? "" : ", ",
+ REFLECT_EXPR_HANDLE (TREE_VEC_ELT (r, i)));
+ pp_printf (&pp, "})");
+ }
else if (eval_is_annotation (r, kind) == boolean_true_node)
pp_printf (&pp, "[[=%E]]",
tree_strip_any_location_wrapper (TREE_VALUE (TREE_VALUE (r))));
@@ -3762,15 +3777,21 @@ remove_const (tree type)
}
/* Process std::meta::annotations_of and annotations_of_with_type.
- Let E be
- -- the corresponding base-specifier if item represents a direct base class
- relationship,
- -- otherwise, the entity represented by item.
+ For a function F, let S(F) be the set of declarations, ignoring any explicit
+ instantiations, that declare either F or a templated function of which F is
+ a specialization.
Returns: A vector containing all of the reflections R representing each
- annotation applying to each declaration of E that precedes either some
- point in the evaluation context or a point immediately following the
- class-specifier of the outermost class for which such a point is in a
- complete-class context.
+ annotation applying to:
+ -- if item represents a function parameter P of a function F, then the
+ declaration of P in each declaration of F in S(F),
+ -- otherwise, if item represents a function F, then each declaration of F
+ in S(F),
+ -- otherwise, if item represents a direct base class relationship (D,B),
+ then the corresponding base-specifier in the definition of D,
+ -- otherwise, each declaration of the entity represented by item,
+ such that precedes either some point in the evaluation context or a point
+ immediately following the class-specifier of the outermost class for which
+ such a point is in a complete-class context.
For any two reflections R1 and R2 in the returned vector, if the annotation
represented by R1 precedes the annotation represented by R2, then R1
appears before R2.
@@ -3779,8 +3800,8 @@ remove_const (tree type)
from T.
Throws: meta::exception unless item represents a type, type alias,
- variable, function, namespace, enumerator, direct base class relationship,
- or non-static data member. */
+ variable, function, function parameter, namespace, enumerator, direct base
+ class relationship, or non-static data member. */
static tree
eval_annotations_of (location_t loc, const constexpr_ctx *ctx, tree r,
@@ -5517,15 +5538,17 @@ eval_variant_alternative (location_t loc
}
/* Process std::meta::data_member_spec.
- Returns: A reflection of a data member description (T,N,A,W,NUA) where
+ Returns: A reflection of a data member description (T,N,A,W,NUA,ANN) where
-- T is the type represented by dealias(type),
-- N is either the identifier encoded by options.name or _|_ if
options.name does not contain a value,
-- A is either the alignment value held by options.alignment or _|_ if
options.alignment does not contain a value,
-- W is either the value held by options.bit_width or _|_ if
- options.bit_width does not contain a value, and
- -- NUA is the value held by options.no_unique_address.
+ options.bit_width does not contain a value,
+ -- NUA is the value held by options.no_unique_address, and
+ -- ANN is the sequence of values constant_of(r) for each r in
+ options.annotations.
Throws: meta::exception unless the following conditions are met:
-- dealias(type) represents either an object type or a reference type;
-- if options.name contains a value, then:
@@ -5537,15 +5560,18 @@ eval_variant_alternative (location_t loc
that is not a keyword when interpreted with the ordinary literal
encoding;
-- if options.name does not contain a value, then options.bit_width
- contains a value;
+ contains a value and options.annotations is empty;
-- if options.bit_width contains a value V, then
-- is_integral_type(type) || is_enum_type(type) is true,
-- options.alignment does not contain a value,
-- options.no_unique_address is false,
-- V is not negative, and
- -- if V equals 0, then options.name does not contain a value; and
+ -- if V equals 0, then options.name does not contain a value;
-- if options.alignment contains a value, it is an alignment value not less
- than alignment_of(type). */
+ than alignment_of(type); and
+ -- for every reflection r in options.annotations, has-type(r) is true,
+ type_of(r) represents a non-array object type, and evaluation of
+ constant_of(r) does not exit via an exception. */
static tree
eval_data_member_spec (location_t loc, const constexpr_ctx *ctx,
@@ -5564,7 +5590,8 @@ eval_data_member_spec (location_t loc, c
*non_constant_p = true;
return NULL_TREE;
}
- tree args[5] = { type, NULL_TREE, NULL_TREE, NULL_TREE, NULL_TREE };
+ tree args[6] = { type, NULL_TREE, NULL_TREE, NULL_TREE, NULL_TREE,
+ NULL_TREE };
for (tree field = next_aggregate_field (TYPE_FIELDS (TREE_TYPE (opts)));
field; field = next_aggregate_field (DECL_CHAIN (field)))
if (tree name = DECL_NAME (field))
@@ -5577,8 +5604,10 @@ eval_data_member_spec (location_t loc, c
args[3] = field;
else if (id_equal (name, "no_unique_address"))
args[4] = field;
+ else if (id_equal (name, "annotations"))
+ args[5] = field;
}
- for (int i = 1; i < 5; ++i)
+ for (int i = 1; i < 6; ++i)
{
if (args[i] == NULL_TREE)
goto fail;
@@ -5602,6 +5631,21 @@ eval_data_member_spec (location_t loc, c
args[i] = boolean_true_node;
continue;
}
+ if (i == 5)
+ {
+ /* To handle annotations, read it using input range from
+ std::vector<info>. */
+ tree rtype
+ = cp_build_reference_type (TREE_TYPE (opt), /*rval*/false);
+ opt = build_address (opt);
+ opt = fold_convert (rtype, opt);
+ opt = get_info_vec (loc, ctx, opt, -1, non_constant_p, overflow_p,
+ jump_target, fun);
+ if (*jump_target || *non_constant_p)
+ return NULL_TREE;
+ args[i] = opt;
+ continue;
+ }
/* Otherwise the member is optional<something>. */
if (!CLASS_TYPE_P (TREE_TYPE (opt)))
goto fail;
@@ -5833,6 +5877,10 @@ eval_data_member_spec (location_t loc, c
return throw_exception (loc, ctx,
"neither name nor bit_width specified",
fun, non_constant_p, jump_target);
+ if (args[1] == NULL_TREE && TREE_VEC_LENGTH (args[5]))
+ return throw_exception (loc, ctx,
+ "no name and non-empty annotations specified",
+ fun, non_constant_p, jump_target);
if (args[3])
{
if (!CP_INTEGRAL_TYPE_P (type) && TREE_CODE (type) != ENUMERAL_TYPE)
@@ -5874,17 +5922,38 @@ eval_data_member_spec (location_t loc, c
"alignment is smaller than alignment_of",
fun, non_constant_p, jump_target);
}
- tree ret = make_tree_vec (5);
+ for (int i = 0; i < TREE_VEC_LENGTH (args[5]); ++i)
+ {
+ tree r = REFLECT_EXPR_HANDLE (TREE_VEC_ELT (args[5], i));
+ reflect_kind kind = REFLECT_EXPR_KIND (TREE_VEC_ELT (args[5], i));
+ if (!has_type (r, kind))
+ return throw_exception (loc, ctx, "reflection does not have a type",
+ fun, non_constant_p, jump_target);
+ tree type = type_of (r, kind);
+ if (eval_is_array_type (loc, type) == boolean_true_node
+ || eval_is_object_type (loc, type) == boolean_false_node)
+ return throw_exception (loc, ctx, "reflection does not have "
+ "non-array object type",
+ fun, non_constant_p, jump_target);