diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc index b816fc45400..1d0a6ba6f5a 100644 --- a/gcc/cp/constexpr.cc +++ b/gcc/cp/constexpr.cc @@ -1322,6 +1322,22 @@ struct constexpr_ctx { mce_value manifestly_const_eval; }; +/* Return ctx->quiet. For use in reflect.cc. */ + +bool +cxx_constexpr_quiet_p (const constexpr_ctx *ctx) +{ + return ctx->quiet; +} + +/* Return ctx->manifestly_const_eval. For use in reflect.cc. */ + +mce_value +cxx_constexpr_manifestly_const_eval (const constexpr_ctx *ctx) +{ + return ctx->manifestly_const_eval; +} + /* Predicates for the meaning of *jump_target. */ static bool diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 16bb39929b5..5509dbc132f 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -9225,6 +9225,8 @@ enum value_cat { extern tree cxx_eval_constant_expression (const constexpr_ctx *, tree, value_cat, bool *, bool *, tree *); +extern bool cxx_constexpr_quiet_p (const constexpr_ctx *); +extern mce_value cxx_constexpr_manifestly_const_eval (const constexpr_ctx *); /* An RAII sentinel used to restrict constexpr evaluation so that it doesn't do anything that causes extra DECL_UID generation. */ diff --git a/gcc/cp/reflect.cc b/gcc/cp/reflect.cc index fd5afb977f8..320fbd468fa 100644 --- a/gcc/cp/reflect.cc +++ b/gcc/cp/reflect.cc @@ -509,8 +509,9 @@ fail_ret: } if (TREE_CODE (TREE_TYPE (deref)) != META_TYPE) { - error_at (loc, "unexpected type %qT of iterator dereference", - TREE_TYPE (deref)); + if (!cxx_constexpr_quiet_p (ctx)) + error_at (loc, "unexpected type %qT of iterator dereference", + TREE_TYPE (deref)); *non_constant_p = true; return call; } @@ -1986,6 +1987,8 @@ type_of (tree r, reflect_kind kind) else if (eval_is_annotation (r) == boolean_true_node) // TODO: or do we need to reflect_constant and get type of that? r = TREE_TYPE (TREE_VALUE (TREE_VALUE (r))); + else if (TREE_CODE (r) == FIELD_DECL && DECL_BIT_FIELD_TYPE (r)) + r = DECL_BIT_FIELD_TYPE (r); else r = TREE_TYPE (r); return r; @@ -4528,6 +4531,41 @@ eval_substitute (location_t loc, const constexpr_ctx *ctx, return get_reflection_raw (loc, ret); } +/* Process std::meta::tuple_size. + Returns: tuple_size_v, where T is the type represented by + dealias(type). */ + +static tree +eval_tuple_size (location_t loc, const constexpr_ctx *ctx, tree type, + tree *jump_target) +{ + if (eval_is_type (type) != boolean_true_node) + return throw_exception_nontype (loc, ctx, type, jump_target); + type = strip_typedefs (type); + /* It's UB to specialize tuple_size_v, so we can use this. */ + return get_tuple_size (type); +} + +/* Process std::meta::tuple_element. + Returns: A reflection representing the type denoted by + tuple_element_t, where T is the type represented by dealias(type) + and I is a constant equal to index. */ + +static tree +eval_tuple_element (location_t loc, const constexpr_ctx *ctx, tree i, + tree type, tree *jump_target) +{ + const unsigned HOST_WIDE_INT index = tree_to_uhwi (i); + if (eval_is_type (type) != boolean_true_node) + return throw_exception_nontype (loc, ctx, type, jump_target); + type = strip_typedefs (type); + type = get_tuple_element_type (type, index); + if (type == error_mark_node) + return error_mark_node; + type = strip_typedefs (type); + return get_reflection_raw (loc, type); +} + /* Process std::meta::data_member_spec. Returns: A reflection of a data member description (T,N,A,W,NUA) where -- T is the type represented by dealias(type), @@ -4911,39 +4949,171 @@ eval_data_member_spec (location_t loc, const constexpr_ctx *ctx, return get_reflection_raw (loc, ret, REFLECT_DATA_MEMBER_SPEC); } -/* Process std::meta::tuple_size. - Returns: tuple_size_v, where T is the type represented by - dealias(type). */ +/* Process std::meta::define_aggregate. + Let C be the class represented by class_type and r_K be the Kth reflection + value in mdescrs. + For every r_K in mdescrs, let (T_K,N_K,A_K,W_K,NUA_K) be the corresponding + data member description represented by r_K. + Constant When: + -- C is incomplete from every point in the evaluation context; + -- is_data_member_spec(r_K) is true for every r_K; + -- is_complete_type(T_K) is true for every r_K; and + -- for every pair (r_K,r_L) where K argument is not a class " + "type reflection"); + *non_constant_p = true; + return call; + } + if (COMPLETE_TYPE_P (type)) + { + if (!cxx_constexpr_quiet_p (ctx)) + error_at (loc, "first % argument is a complete " + "class type reflection"); + *non_constant_p = true; + return call; + } + hash_set nameset; + for (int i = 0; i < TREE_VEC_LENGTH (rvec); ++i) + { + tree ra = TREE_VEC_ELT (rvec, i); + tree a = REFLECT_EXPR_HANDLE (ra); + if (REFLECT_EXPR_KIND (ra) != REFLECT_DATA_MEMBER_SPEC) + { + if (!cxx_constexpr_quiet_p (ctx)) + error_at (loc, "% argument not a data member " + "description"); + *non_constant_p = true; + return call; + } + if (eval_is_complete_type (TREE_VEC_ELT (a, 0)) != boolean_true_node) + { + if (!cxx_constexpr_quiet_p (ctx)) + error_at (loc, "% argument data member " + "description without complete type"); + *non_constant_p = true; + return call; + } + if (TREE_VEC_ELT (a, 1) + && !id_equal (TREE_VEC_ELT (a, 1), "_") + && nameset.add (TREE_VEC_ELT (a, 1))) + { + if (!cxx_constexpr_quiet_p (ctx)) + error_at (loc, "name %qD used in multiple data member " + "descriptions", TREE_VEC_ELT (a, 1)); + *non_constant_p = true; + return call; + } + if (TYPE_WARN_IF_NOT_ALIGN (type) + && TREE_VEC_ELT (a, 3)) + { + if (!cxx_constexpr_quiet_p (ctx)) + error_at (loc, "cannot declare bit-field in " + "% type"); + *non_constant_p = true; + return call; + } + } + if (cxx_constexpr_manifestly_const_eval (ctx) != mce_true) + { + /* If define_aggregate is evaluated multiple times, + the second invocation with the same arguments will + necessarily fail. Limit those to manifestly + constant-evaluation. */ + if (!cxx_constexpr_quiet_p (ctx)) + error_at (loc, "% used outside of " + "manifestly constant-evaluation"); + *non_constant_p = true; + return call; + } + iloc_sentinel ils = loc; type = strip_typedefs (type); - /* It's UB to specialize tuple_size_v, so we can use this. */ - return get_tuple_size (type); -} - -/* Process std::meta::tuple_element. - Returns: A reflection representing the type denoted by - tuple_element_t, where T is the type represented by dealias(type) - and I is a constant equal to index. */ - -static tree -eval_tuple_element (location_t loc, const constexpr_ctx *ctx, tree i, - tree type, tree *jump_target) -{ - const unsigned HOST_WIDE_INT index = tree_to_uhwi (i); - if (eval_is_type (type) != boolean_true_node) - return throw_exception_nontype (loc, ctx, type, jump_target); - type = strip_typedefs (type); - type = get_tuple_element_type (type, index); - if (type == error_mark_node) - return error_mark_node; - type = strip_typedefs (type); - return get_reflection_raw (loc, type); + type = TYPE_MAIN_VARIANT (type); + if (primary_template_specialization_p (type)) + { + type = maybe_process_partial_specialization (type); + if (type == error_mark_node) + { + *non_constant_p = true; + return call; + } + } + if (!TYPE_BINFO (type)) + xref_basetypes (type, NULL_TREE); + pushclass (type); + gcc_assert (!TYPE_FIELDS (type)); + tree fields = NULL_TREE; + for (int i = 0; i < TREE_VEC_LENGTH (rvec); ++i) + { + tree ra = TREE_VEC_ELT (rvec, i); + tree a = REFLECT_EXPR_HANDLE (ra); + tree f = build_decl (cp_expr_loc_or_input_loc (ra), FIELD_DECL, + TREE_VEC_ELT (a, 1), TREE_VEC_ELT (a, 0)); + DECL_CHAIN (f) = fields; + DECL_IN_AGGR_P (f) = 1; + DECL_CONTEXT (f) = type; + TREE_PUBLIC (f) = 1; + if (TREE_VEC_ELT (a, 3)) + { + /* Temporarily stash the width in DECL_BIT_FIELD_REPRESENTATIVE. + check_bitfield_decl picks it from there later and sets DECL_SIZE + accordingly. */ + DECL_BIT_FIELD_REPRESENTATIVE (f) = TREE_VEC_ELT (a, 3); + SET_DECL_C_BIT_FIELD (f); + } + else if (TREE_VEC_ELT (a, 2)) + { + SET_DECL_ALIGN (f, tree_to_uhwi (TREE_VEC_ELT (a, 2)) + * BITS_PER_UNIT); + DECL_USER_ALIGN (f) = 1; + } + if (TREE_VEC_ELT (a, 4) == boolean_true_node) + { + tree attr = build_tree_list (NULL_TREE, + get_identifier ("no_unique_address")); + attr = build_tree_list (attr, NULL_TREE); + cplus_decl_attributes (&f, attr, 0); + } + fields = f; + } + TYPE_FIELDS (type) = fields; + finish_struct (type, NULL_TREE); + return get_reflection_raw (loc, orig_type); } /* Expand a call to a metafunction. CALL is the CALL_EXPR. @@ -5602,6 +5772,20 @@ process_metafunction (const constexpr_ctx *ctx, tree call, if (id_equal (name, "u8identifier_of")) return eval_identifier_of (loc, ctx, h, kind, jump_target, char8_type_node, TREE_TYPE (call)); + if (id_equal (name, "tuple_size")) + { + tree tsize = eval_tuple_size (loc, ctx, h, jump_target); + if (*jump_target) + return NULL_TREE; + if (!tsize) + { + if (!cxx_constexpr_quiet_p (ctx)) + error_at (loc, "couldn%'t compute %qs of %qT", "tuple_size", h); + *non_constant_p = true; + return call; + } + return tsize; + } if (id_equal (name, "can_substitute")) { tree hvec = get_info_vec (loc, ctx, call, 1, non_constant_p, @@ -5635,18 +5819,15 @@ process_metafunction (const constexpr_ctx *ctx, tree call, return eval_data_member_spec (loc, ctx, h, opts, call, non_constant_p, overflow_p, jump_target); } - if (id_equal (name, "tuple_size")) + if (id_equal (name, "define_aggregate")) { - tree tsize = eval_tuple_size (loc, ctx, h, jump_target); + tree hvec = get_info_vec (loc, ctx, call, 1, non_constant_p, + overflow_p, jump_target); if (*jump_target) return NULL_TREE; - if (!tsize) - { - error_at (loc, "couldn%'t compute %qs of %qT", "tuple_size", h); - *non_constant_p = true; - return call; - } - return tsize; + if (*non_constant_p) + return call; + return eval_define_aggregate (loc, ctx, h, hvec, call, non_constant_p); } not_found: diff --git a/gcc/testsuite/g++.dg/reflect/data_member_spec1.C b/gcc/testsuite/g++.dg/reflect/data_member_spec1.C index fb2d7e7c363..9159f22eaa3 100644 --- a/gcc/testsuite/g++.dg/reflect/data_member_spec1.C +++ b/gcc/testsuite/g++.dg/reflect/data_member_spec1.C @@ -94,6 +94,10 @@ static_assert (!valid_data_member_spec (^^int, { .name = "static_assert" })); static_assert (!valid_data_member_spec (^^int, { .name = "__is_convertible" })); static_assert (!valid_data_member_spec (^^int, { .name = "__builtin_is_nothrow_relocatable" })); static_assert (!valid_data_member_spec (^^int, { .name = "007" })); +static_assert (!valid_data_member_spec (^^int, { .name = "operator++" })); +static_assert (!valid_data_member_spec (^^int, { .name = "operator ++" })); +static_assert (!valid_data_member_spec (^^int, { .name = "foo\\u00AA" })); +static_assert (!valid_data_member_spec (^^int, { .name = "+ -" })); static_assert (valid_data_member_spec (^^int, { .name = u8"\u00AA" })); static_assert (!valid_data_member_spec (^^int, { .name = u8"\u00AB" })); static_assert (!valid_data_member_spec (^^int, { .name = u8"\u00B6" })); diff --git a/gcc/testsuite/g++.dg/reflect/define_aggregate1.C b/gcc/testsuite/g++.dg/reflect/define_aggregate1.C new file mode 100644 index 00000000000..ec5a6e99d03 --- /dev/null +++ b/gcc/testsuite/g++.dg/reflect/define_aggregate1.C @@ -0,0 +1,108 @@ +// { dg-do compile { target c++26 } } +// { dg-additional-options "-freflection" } +// Test std::meta::define_aggregate. + +#include +#include + +using namespace std::meta; + +enum E { E0, E1 }; +struct S0 {}; +struct S1; +struct S2; +struct S3; +struct S4; +struct S5; +union U1; +template +struct S6; +template +struct S7 { long e; short f; }; +struct S8; +struct S9; +using A9 = S9; + +consteval { + if (define_aggregate (^^S1, {}) != ^^S1) + throw 1; + if (define_aggregate (^^S2, { data_member_spec (^^S1, { .name = "bar" }), + data_member_spec (^^long, { .name = u8"baz", + .alignment = 2 * alignof (long), + .no_unique_address = true }), + data_member_spec (^^unsigned int, { .bit_width = 7 }), + data_member_spec (^^E, { .name = "extremely_long_identifier1", + .bit_width = 6 }), + data_member_spec (^^int, { .bit_width = 0 }), + data_member_spec (^^const E *, { .name = u8"extremely_long_identifier2", + .alignment = 2 * alignof (E *) }) }) != ^^S2) + throw 2; + if (define_aggregate (^^S3, { data_member_spec (^^S0, { .name = "a", + .no_unique_address = true }), + data_member_spec (^^S1, { .name = "b", + .no_unique_address = true }) }) != ^^S3) + throw 3; + if (define_aggregate (^^S4, { data_member_spec (^^S0, { .name = "c" }), + data_member_spec (^^S1, { .name = "d" }) }) != ^^S4) + throw 4; + if (define_aggregate (^^S5, { data_member_spec (^^const E &, { .name = u8"qu\N{LATIN SMALL LETTER AE}" }), + data_member_spec (^^const E &, { .name = u8"foo" }) }) != ^^S5) + throw 5; + if (define_aggregate (^^U1, { data_member_spec (^^int, { .name = u8"_" }), + data_member_spec (^^long long, { .name = "abc" }) }) != ^^U1) + throw 6; + if (define_aggregate (^^S6 <42>, { data_member_spec (^^int, { .name = "a" }), + data_member_spec (^^long, { .name = "b" }) }) != ^^S6 <42>) + throw 7; + if (define_aggregate (substitute (^^S6, { reflect_constant (43) }), + { data_member_spec (^^long, { .name = "c" }), + data_member_spec (^^unsigned int, { .name = "d", .bit_width = 3 }) }) != ^^S6 <43>) + throw 8; + if (define_aggregate (^^S7 <42>, { data_member_spec (^^short, { .name = "g" }), + data_member_spec (^^float, { .name = "h" }) }) != ^^S7 <42>) + throw 9; + if (define_aggregate (^^const S8, { data_member_spec (^^U1, { .name = "u" }) }) != ^^const S8) + throw 10; + if (define_aggregate (^^A9, { data_member_spec (^^U1, { .name = "u" }) }) != ^^A9) + throw 10; +} + +constexpr E e0 = E0, e1 = E1; +S2 s2 = { .bar = {}, .baz = 42LL, .extremely_long_identifier1 = E1, .extremely_long_identifier2 = &e0 }; +S3 s3 = { .a = {}, .b = {} }; +S4 s4 = { .c = {}, .d = {} }; +constexpr S5 s5 = { e0, e1 }; +U1 u1 = { ._ = 5 }, u1a = { .abc = 42LL }; +S6 <42> s642 = { .a = 1, .b = 2 }; +S6 <43> s643 = { .c = 6, .d = 7 }; +S7 <42> s742 = { .g = 5, .h = 6.0f }; +S7 <43> s743 = { .e = 8, .f = 9 }; +S8 s8 = { .u = { .abc = 2LL } }; +S9 s9 = { .u = { ._ = 3 } }; +consteval { + S6 <43> x; + x.c = -1; + x.d = 7; + ++x.d; + if (x.d != 0) + throw 11; + --x.d; + if (x.d != 7) + throw 12; +} +static_assert (type_of (^^S2::bar) == ^^S1); +static_assert (type_of (^^S2::baz) == ^^long); +static_assert (type_of (^^S2::extremely_long_identifier1) == ^^E); +static_assert (type_of (^^S2::extremely_long_identifier2) == ^^const E *); +static_assert (offsetof (S2, bar) == 0); +static_assert (offsetof (S2, baz) >= 2 * alignof (long) && offsetof (S2, baz) % (2 * alignof (long)) == 0); +static_assert (offsetof (S2, extremely_long_identifier2) > offsetof (S2, baz)); +static_assert (offsetof (S2, extremely_long_identifier2) % (2 * alignof (E)) == 0); +static_assert (type_of (^^S3::a) == ^^S0); +static_assert (type_of (^^S3::b) == ^^S1); +static_assert (sizeof (S3) == sizeof (S0) && sizeof (S4) == 2 * sizeof (S0)); +static_assert (type_of (^^S4::c) == ^^S0); +static_assert (type_of (^^S4::d) == ^^S1); +static_assert (s5.qu\u00E6 == E0 && s5.foo == E1); +static_assert (type_of (^^S5::qu\u00E6) == ^^const E &); +static_assert (type_of (^^S5::foo) == ^^const E &); diff --git a/gcc/testsuite/g++.dg/reflect/define_aggregate2.C b/gcc/testsuite/g++.dg/reflect/define_aggregate2.C new file mode 100644 index 00000000000..c6ce20d9003 --- /dev/null +++ b/gcc/testsuite/g++.dg/reflect/define_aggregate2.C @@ -0,0 +1,32 @@ +// { dg-do compile { target c++26 } } +// { dg-additional-options "-freflection" } +// Test std::meta::define_aggregate. + +#include +#include + +using namespace std::meta; + +enum E { E0, E1 }; +struct S0 {}; +struct S1; +struct S2; +struct S3; +struct S4; +struct S5; +template +struct S7 { long e; short f; }; +S7 <15> s715; +consteval { define_aggregate (^^::, {}); } // { dg-error "first 'define_aggregate' argument is not a class type reflection" } +consteval { define_aggregate (^^int, {}); } // { dg-error "first 'define_aggregate' argument is not a class type reflection" } +consteval { define_aggregate (^^E, {}); } // { dg-error "first 'define_aggregate' argument is not a class type reflection" } +consteval { define_aggregate (^^S0, {}); } // { dg-error "first 'define_aggregate' argument is a complete class type reflection" } +consteval { define_aggregate (^^S7 <15>, {}); } // { dg-error "first 'define_aggregate' argument is a complete class type reflection" } +consteval { define_aggregate (^^S1, { ^^int }); } // { dg-error "'define_aggregate' argument not a data member description" } +consteval { define_aggregate (^^S2, { data_member_spec (^^S5, { .name = "a" }) }); } // { dg-error "'define_aggregate' argument data member description without complete type" } +consteval { define_aggregate (^^S3, { data_member_spec (^^int, { .name = "a" }), // { dg-error "name 'a' used in multiple data member descriptions" } + data_member_spec (^^long, { .name = "a" }) }); } +consteval { define_aggregate (^^S4, { data_member_spec (^^int, { .name = u8"_" }), + data_member_spec (^^long, { .name = u8"_" }) }); } +constexpr S4 s4 = { 1, 2 }; +consteval { auto a = s4._; } // { dg-error "request for member '_' is ambiguous" } diff --git a/gcc/testsuite/g++.dg/reflect/type_of1.C b/gcc/testsuite/g++.dg/reflect/type_of1.C index c4a43a90f36..c84464dab23 100644 --- a/gcc/testsuite/g++.dg/reflect/type_of1.C +++ b/gcc/testsuite/g++.dg/reflect/type_of1.C @@ -24,6 +24,7 @@ struct T { }; struct U { int u; + int v : 5; }; template struct TCls {}; template void TFn(); @@ -113,6 +114,7 @@ static_assert (type_of (^^fn) == ^^void ()); static_assert (type_of (^^Enum::A) == ^^Enum); static_assert (type_of (^^A) == ^^Enum); static_assert (type_of (^^S::mem) == ^^int); +static_assert (type_of (^^U::v) == ^^int); //static_assert (type_of (std::meta::members_of (^^S, ctx)[1]) == ??); //static_assert (type_of (std::meta::bases_of (^^S, ctx)[0]) == ??); static_assert (type_of (std::meta::data_member_spec (^^int, { .name = "member" })) == ^^int); diff --git a/libstdc++-v3/include/std/meta b/libstdc++-v3/include/std/meta index b04407eb565..2e1265fd4f2 100644 --- a/libstdc++-v3/include/std/meta +++ b/libstdc++-v3/include/std/meta @@ -328,6 +328,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION }; consteval info data_member_spec(info, data_member_options); consteval bool is_data_member_spec(info); + template> + consteval info define_aggregate(info, _R&&); // associated with [meta.unary.cat], primary type categories consteval bool is_void_type(info);