diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index c7b9352b083..16bb39929b5 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -7418,6 +7418,8 @@ extern bool check_array_initializer (tree, tree, tree); extern void omp_declare_variant_finalize (tree, tree); struct cp_decomp { tree decl; unsigned int count; }; extern void cp_finish_decl (tree, tree, bool, tree, int, cp_decomp * = nullptr); +extern tree get_tuple_size (tree); +extern tree get_tuple_element_type (tree, unsigned HOST_WIDE_INT); extern tree lookup_decomp_type (tree); HOST_WIDE_INT cp_decomp_size (location_t, tree, tsubst_flags_t); extern bool cp_finish_decomp (tree, cp_decomp *, bool = false); diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc index 02e8cef6b2e..16df6bcc725 100644 --- a/gcc/cp/decl.cc +++ b/gcc/cp/decl.cc @@ -10379,7 +10379,7 @@ find_decomp_class_base (location_t loc, tree type, tree ret, /* Return std::tuple_size::value. */ -static tree +tree get_tuple_size (tree type) { tree args = make_tree_vec (1); @@ -10407,7 +10407,7 @@ get_tuple_size (tree type) /* Return std::tuple_element::type. */ -static tree +tree get_tuple_element_type (tree type, unsigned HOST_WIDE_INT i) { tree args = make_tree_vec (2); diff --git a/gcc/cp/reflect.cc b/gcc/cp/reflect.cc index 208975b77fa..ed2e195e36a 100644 --- a/gcc/cp/reflect.cc +++ b/gcc/cp/reflect.cc @@ -4911,6 +4911,43 @@ 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). */ + +static tree +eval_tuple_size (location_t loc, const constexpr_ctx *ctx, tree type, + tree *jump_target) +{ + type = eval_dealias (loc, ctx, type, jump_target); + if (*jump_target) + return type; + type = REFLECT_EXPR_HANDLE (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); + type = eval_dealias (loc, ctx, type, jump_target); + if (*jump_target) + return type; + type = REFLECT_EXPR_HANDLE (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); +} + /* Expand a call to a metafunction. CALL is the CALL_EXPR. JUMP_TARGET is set if we are throwing std::meta::exception. */ @@ -4958,6 +4995,32 @@ process_metafunction (const constexpr_ctx *ctx, tree call, id_equal (name, "symbol_of") ? char_type_node : char8_type_node, TREE_TYPE (call)); } + if (id_equal (name, "tuple_element")) + { + tree i = get_nth_callarg (call, 0); + location_t loc = cp_expr_loc_or_input_loc (i); + i = cxx_eval_constant_expression (ctx, i, vc_prvalue, + non_constant_p, overflow_p, + jump_target); + if (*jump_target) + return NULL_TREE; + if (*non_constant_p) + return call; + tree type = get_info (ctx, call, 1, non_constant_p, overflow_p, + jump_target); + if (*jump_target) + return NULL_TREE; + if (*non_constant_p) + return call; + type = REFLECT_EXPR_HANDLE (type); + type = eval_tuple_element (loc, ctx, i, type, jump_target); + if (type == error_mark_node) + { + *non_constant_p = true; + return call; + } + return type; + } tree info = get_info (ctx, call, 0, non_constant_p, overflow_p, jump_target); if (*jump_target) @@ -5577,6 +5640,23 @@ 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")) + { + tree tsize = eval_tuple_size (loc, ctx, h, jump_target); + if (*jump_target) + return NULL_TREE; + if (!tsize) + { + if (TYPE_P (h)) + error_at (loc, "couldn%'t compute %qs of %qT", "tuple_size", h); + else + error_at (loc, "couldn%'t compute %qs of non-type %qE", + "tuple_size", h); + *non_constant_p = true; + return call; + } + return tsize; + } not_found: sorry ("%qE", name); diff --git a/gcc/testsuite/g++.dg/reflect/tuple1.C b/gcc/testsuite/g++.dg/reflect/tuple1.C new file mode 100644 index 00000000000..2357eeb64d7 --- /dev/null +++ b/gcc/testsuite/g++.dg/reflect/tuple1.C @@ -0,0 +1,86 @@ +// { dg-do compile { target c++26 } } +// { dg-additional-options "-freflection" } +// Test std::meta::tuple_{size,element}. + +#include +#include +#include +#include +#include + +using namespace std::meta; + +struct E {}; + +using Tup0 = std::tuple<>; +static_assert (tuple_size (^^Tup0) == 0); + +using Tup1 = std::tuple; +static_assert (tuple_size (^^Tup1) == 1); +static_assert (tuple_element (0, ^^Tup1) == ^^int); + +using Tup2 = std::tuple; +static_assert (tuple_size (^^Tup2) == 2); +static_assert (tuple_element (0, ^^Tup2) == ^^int); +static_assert (tuple_element (1, ^^Tup2) == ^^bool); + +using Tup3 = std::tuple; +static_assert (tuple_size (^^Tup3) == 3); +static_assert (tuple_element (0, ^^Tup3) == ^^int); +static_assert (tuple_element (1, ^^Tup3) == ^^bool); +static_assert (tuple_element (2, ^^Tup3) == ^^char); + +using Tup10 = std::tuple; +static_assert (tuple_size (^^Tup10) == 10); +static_assert (tuple_element (0, ^^Tup10) == ^^int); +static_assert (tuple_element (9, ^^Tup10) == ^^int); + +using mytype1 = float; +using mytype2 = mytype1 *; +static_assert (tuple_size (^^std::tuple) == 2); +static_assert (tuple_element (0, ^^std::tuple) == ^^float); +static_assert (tuple_element (1, ^^std::tuple) == ^^float *); + +static_assert (tuple_size (^^std::tuple<>) == 0); +static_assert (tuple_size (^^std::tuple) == 1); +static_assert (tuple_size (^^std::tuple) == 1); +static_assert (tuple_size (^^std::tuple>) == 1); +static_assert (tuple_size (^^const std::tuple<>) == 0); +static_assert (tuple_size (^^const std::tuple) == 1); +static_assert (tuple_size (^^const std::tuple) == 1); +static_assert (tuple_size (^^const std::tuple>) == 1); + +using Arr5 = std::array; +static_assert (tuple_size (^^Arr5) == 5); +static_assert (tuple_size (^^const Arr5) == 5); +static_assert (tuple_element (0, ^^Arr5) == ^^int); +static_assert (tuple_element (1, ^^Arr5) == ^^int); +static_assert (tuple_element (2, ^^Arr5) == ^^int); +static_assert (tuple_element (3, ^^Arr5) == ^^int); +static_assert (tuple_element (4, ^^Arr5) == ^^int); +using Arr0 = std::array; +static_assert (tuple_size (^^Arr0) == 0); + +using Pair = std::pair; +// Always 2. +static_assert (tuple_size (^^Pair) == 2); +static_assert (tuple_element (0, ^^Pair) == ^^E); +static_assert (tuple_element (1, ^^Pair) == ^^int); + +using C = std::complex; +static_assert (tuple_size (^^C) == 2); +static_assert (tuple_element (0, ^^C) == ^^double); +static_assert (tuple_element (1, ^^C) == ^^double); + +using S1 = std::ranges::subrange; +using S2 = std::ranges::subrange; +static_assert (tuple_size (^^S1) == 2); +static_assert (tuple_size (^^S2) == 2); +static_assert (tuple_element (0, ^^S1) == ^^int *); +static_assert (tuple_element (1, ^^S1) == ^^int *); +static_assert (tuple_element (0, ^^const S1) == ^^int *); +static_assert (tuple_element (1, ^^const S1) == ^^int *); +static_assert (tuple_element (0, ^^S2) == ^^long *); +static_assert (tuple_element (1, ^^S2) == ^^void *); +static_assert (tuple_element (0, ^^const S2) == ^^long *); +static_assert (tuple_element (1, ^^const S2) == ^^void *); diff --git a/gcc/testsuite/g++.dg/reflect/tuple2.C b/gcc/testsuite/g++.dg/reflect/tuple2.C new file mode 100644 index 00000000000..75491324f03 --- /dev/null +++ b/gcc/testsuite/g++.dg/reflect/tuple2.C @@ -0,0 +1,21 @@ +// { dg-do compile { target c++26 } } +// { dg-additional-options "-freflection" } +// Test std::meta::tuple_{size,element}. + +#include +#include + +using namespace std::meta; + +constexpr auto s1 = tuple_size (^^int); // { dg-error "compute .tuple_size. of .int." } +int x; +constexpr auto s2 = tuple_size (^^x); // { dg-error "compute .tuple_size. of non-type .x." } + +constexpr auto r1 = tuple_element (666, ^^std::tuple); + +using Arr0 = std::array; +constexpr auto r2 = tuple_element (1, ^^Arr0); + +// { dg-error "tuple index must be in range" "" { target *-*-* } 0 } +// { dg-error "array index is in range" "" { target *-*-* } 0 } +// { dg-error "pack index .666." "" { target *-*-* } 0 } diff --git a/libstdc++-v3/include/std/meta b/libstdc++-v3/include/std/meta index 86b094788b5..b04407eb565 100644 --- a/libstdc++-v3/include/std/meta +++ b/libstdc++-v3/include/std/meta @@ -466,6 +466,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION consteval info decay(info); consteval info underlying_type(info); + consteval size_t tuple_size(info); + consteval info tuple_element(size_t, info); + consteval strong_ordering type_order(info, info); // [meta.reflection.annotation], annotation reflection