On Tue, 29 Oct 2024, Marek Polacek wrote: > On Tue, Oct 22, 2024 at 07:42:57PM -0400, Jason Merrill wrote: > > On 10/22/24 3:22 PM, Marek Polacek wrote: > > > Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk? > > > > > > -- >8 -- > > > This patch implements C++26 Pack Indexing, as described in > > > <https://wg21.link/P2662R3>. > > > > Great! > > > > > The issue discussing how to mangle pack indexes has not been resolved > > > yet <https://github.com/itanium-cxx-abi/cxx-abi/issues/175> and I've > > > made no attempt to address it so far. > > > > > > Rather than introducing a new template code for a pack indexing, I'm > > > adding a new operand to EXPR_PACK_EXPANSION to store the index; for > > > TYPE_PACK_EXPANSION, I'm stashing the index into TYPE_VALUES_RAW. This > > > feature is akin to __type_pack_element, so they can share the element > > > extraction part. > > In v2, I'm using two new codes, as discussed elsewhere. > > > > A pack indexing in a decltype proved to be a bit tricky; eventually, > > > I've added PACK_EXPANSION_PARENTHESIZED_P -- while parsing, we can't > > > really tell what it's going to expand to. > > > > As I comment below, I think we should have enough information while parsing; > > what it expands to doesn't matter. > > Yup; I must not have realized that pack-index-expression is a product of > id-expression. > > > > With this feature, it's valid to write something like > > > > > > using U = tmpl<Ts...[Is]...>; > > > > > > where we first expand the template argument into > > > > > > Ts...[Is#0], Ts...[Is#1], ... > > > > > > and then substitute each individual pack index. > > > > > > I have no test for the module.cc code, that is just guesswork. > > > > Looks straightforward enough. > > It was. I made sure with an assert that the new code is exercised. > > > > @@ -2605,6 +2605,8 @@ write_type (tree type) > > > case TYPE_PACK_EXPANSION: > > > write_string ("Dp"); > > > write_type (PACK_EXPANSION_PATTERN (type)); > > > + /* TODO: Mangle PACK_EXPANSION_INDEX > > > + <https://github.com/itanium-cxx-abi/cxx-abi/issues/175> */ > > > > Could we warn about this so it doesn't get forgotten? And similarly in > > write_expression? > > There is now a new sorry. > > > > @@ -3952,7 +3953,11 @@ find_parameter_packs_r (tree *tp, int > > > *walk_subtrees, void* data) > > > break; > > > case VAR_DECL: > > > - if (DECL_PACK_P (t)) > > > + /* We can have > > > + T...[0] a; > > > + (T...[0])(a); // #1 > > > + where the 'a' in #1 is not a bare parameter pack. */ > > > + if (DECL_PACK_P (t) && !PACK_EXPANSION_INDEX (TREE_TYPE (t))) > > > > Seems like the INDEX check should move into DECL_PACK_P? > > > > Why doesn't this apply to PARM_DECL above? > > I think this is now moot. > > > > @@ -13946,6 +13969,10 @@ tsubst_pack_expansion (tree t, tree args, > > > tsubst_flags_t complain, > > > && PACK_EXPANSION_P (TREE_VEC_ELT (result, 0))) > > > return TREE_VEC_ELT (result, 0); > > > + /* C++26 Pack Indexing. */ > > > + if (index) > > > + return pack_index_element (index, result, complain); > > > > Could we only compute the desired element rather than computing all of them > > and selecting the desired one? > > I don't think so. Especially now that the PACK_EXPANSION_P is just one > operand of a PACK_INDEX_*, and tsubst_pack_expansion is agnostic about > whether the expansion is part of a pack index. > > > > @@ -16897,17 +16924,23 @@ tsubst (tree t, tree args, tsubst_flags_t > > > complain, tree in_decl) > > > ctx = tsubst_pack_expansion (ctx, args, > > > complain | tf_qualifying_scope, > > > in_decl); > > > - if (ctx == error_mark_node > > > - || TREE_VEC_LENGTH (ctx) > 1) > > > + if (ctx == error_mark_node) > > > return error_mark_node; > > > - if (TREE_VEC_LENGTH (ctx) == 0) > > > + /* If there was a pack-index-specifier, we won't get a TREE_VEC, > > > + just the single element. */ > > > + if (TREE_CODE (ctx) == TREE_VEC) > > > { > > > - if (complain & tf_error) > > > - error ("%qD is instantiated for an empty pack", > > > - TYPENAME_TYPE_FULLNAME (t)); > > > - return error_mark_node; > > > + if (TREE_VEC_LENGTH (ctx) > 1) > > > + return error_mark_node; > > > > This is preexisting, but it seems like we're missing a call to error() in > > this case. > > Added. > > > > @@ -17041,13 +17074,20 @@ tsubst (tree t, tree args, tsubst_flags_t > > > complain, tree in_decl) > > > else > > > { > > > bool id = DECLTYPE_TYPE_ID_EXPR_OR_MEMBER_ACCESS_P (t); > > > - if (id && TREE_CODE (DECLTYPE_TYPE_EXPR (t)) == BIT_NOT_EXPR > > > - && EXPR_P (type)) > > > + tree op = DECLTYPE_TYPE_EXPR (t); > > > + if (id && TREE_CODE (op) == BIT_NOT_EXPR && EXPR_P (type)) > > > /* In a template ~id could be either a complement > > > expression > > > or an unqualified-id naming a destructor; if > > > instantiating > > > it produces an expression, it's not an id-expression or > > > member access. */ > > > id = false; > > > + /* With pack indexing, we don't know what it's going to expand to > > > + until instantiation. The intent is that a pack indexing > > > + expression behaves exactly as the underlying expression > > > + would. */ > > > + else if (PACK_EXPANSION_P (op)) > > > + id = (!PACK_EXPANSION_PARENTHESIZED_P (op) > > > + && unparenthesized_id_or_class_member_access_p (type)); > > > > Why isn't the value of DECLTYPE_TYPE_ID_EXPR_OR_MEMBER_ACCESS_P already > > accurate in this case? It seems to me that 'id' should be based on the > > syntax of the decltype, not what the substitution produces. > > > > And so maybe we don't need PACK_EXPANSION_PARENTHESIZED_P except maybe for > > decltype(auto)? > > I think this should be resolved. I think I need PACK_INDEX_PARENTHESIZED_P > for both decltype and decltype(auto). Previously I wasn't testing > decltype(auto) at all, so I've added new test. > > > > @@ -17074,6 +17114,11 @@ tsubst (tree t, tree args, tsubst_flags_t > > > complain, tree in_decl) > > > case NONTYPE_ARGUMENT_PACK: > > > return tsubst_argument_pack (t, args, complain, in_decl); > > > + case TYPE_PACK_EXPANSION: > > > + if (PACK_EXPANSION_INDEX (t)) > > > + return tsubst_pack_expansion (t, args, complain, in_decl); > > > + gcc_fallthrough (); > > > > Let's gcc_unreachable here rather than fallthrough to unreachable. > > Done. > > > > @@ -19596,6 +19641,8 @@ tsubst_stmt (tree t, tree args, tsubst_flags_t > > > complain, tree in_decl) > > > } > > > case EXPR_PACK_EXPANSION: > > > + if (PACK_EXPANSION_INDEX (t)) > > > + RETURN (tsubst_pack_expansion (t, args, complain, in_decl)); > > > error ("invalid use of pack expansion expression"); > > > RETURN (error_mark_node); > > > @@ -21776,6 +21823,8 @@ tsubst_expr (tree t, tree args, tsubst_flags_t > > > complain, tree in_decl) > > > } > > > case EXPR_PACK_EXPANSION: > > > + if (PACK_EXPANSION_INDEX (t)) > > > + RETURN (tsubst_pack_expansion (t, args, complain, in_decl)); > > > error ("invalid use of pack expansion expression"); > > > RETURN (error_mark_node); > > > > Preexisting, but do we need this case in both _stmt and _expr? > > Looks like I don't need the _stmt case; dropped. > > > > @@ -27685,6 +27734,14 @@ tsubst_initializer_list (tree t, tree argvec) > > > NULL_TREE); > > > if (expanded_bases == error_mark_node) > > > continue; > > > + /* If there was a pack-index-specifier, we won't get > > > + a TREE_VEC but the rest of the code assumes so. */ > > > > Hmm, this difference in return value seems fragile, but I guess you only > > actually need to compensate for it in a couple of places... > > This doesn't happen anymore due to the new codes. > > > > @@ -4181,7 +4183,9 @@ finish_base_specifier (tree base, tree access, bool > > > virtual_p) > > > error ("invalid base-class specification"); > > > result = NULL_TREE; > > > } > > > - else if (! MAYBE_CLASS_TYPE_P (base)) > > > + else if (! MAYBE_CLASS_TYPE_P (base) > > > + && ! (PACK_EXPANSION_P (base) > > > + && PACK_EXPANSION_INDEX (base))) > > > > Instead of this change, this case should be added to WILDCARD_TYPE_P. > > Done. > > > > -/* Implement the __type_pack_element keyword: Return the type > > > - at index IDX within TYPES. */ > > > +/* Return the type at index IDX within TYPES. */ > > > static tree > > > -finish_type_pack_element (tree idx, tree types, tsubst_flags_t complain) > > > +get_vec_elt_checking (tree idx, tree types, bool pack_index_p, > > > + tsubst_flags_t complain) > > > { > > > idx = maybe_constant_value (idx); > > > if (TREE_CODE (idx) != INTEGER_CST || !INTEGRAL_TYPE_P (TREE_TYPE > > > (idx))) > > > { > > > if (complain & tf_error) > > > - error ("%<__type_pack_element%> index is not an integral constant"); > > > + { > > > + if (pack_index_p) > > > + error ("pack index is not an integral constant"); > > > + else > > > + error ("%<__type_pack_element%> index is not an integral constant"); > > > > Maybe just always say "pack index"? > > Yeah. Done. > > > > @@ -5575,12 +5581,16 @@ cp_walk_subtrees (tree *tp, int *walk_subtrees_p, > > > walk_tree_fn func, > > > case TYPE_PACK_EXPANSION: > > > WALK_SUBTREE (TREE_TYPE (t)); > > > WALK_SUBTREE (PACK_EXPANSION_EXTRA_ARGS (t)); > > > + if (PACK_EXPANSION_INDEX (t)) > > > + WALK_SUBTREE (PACK_EXPANSION_INDEX (t)); > > > > I don't think it's necessary to check non-null before WALK_SUBTREE. > > Fixed. > > I've added a number of tests. I've not handled the mangling part, though. > > Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk? > > -- >8 -- > This patch implements C++26 Pack Indexing, as described in > <https://wg21.link/P2662R3>. > > The issue discussing how to mangle pack indexes has not been resolved > yet <https://github.com/itanium-cxx-abi/cxx-abi/issues/175> and I've > made no attempt to address it so far. > > Unlike v1, which used augmented TYPE/EXPR_PACK_EXPANSION codes, this > version introduces two new codes: PACK_INDEX_EXPR and PACK_INDEX_TYPE. > Both carry two operands: the pack expansion and the index. They are > handled in tsubst_pack_index: substitute the index and the pack and > then extract the element from the vector (if possible). > > To handle pack indexing in a decltype or with decltype(auto), there is > also the new PACK_INDEX_PARENTHESIZED_P flag. > > With this feature, it's valid to write something like > > using U = tmpl<Ts...[Is]...>; > > where we first expand the template argument into > > Ts...[Is#0], Ts...[Is#1], ... > > and then substitute each individual pack index. > > PR c++/113798 > > gcc/cp/ChangeLog: > > * constexpr.cc (potential_constant_expression_1) <case PACK_INDEX_EXPR>: > New case. > * cp-objcp-common.cc (cp_common_init_ts): Mark PACK_INDEX_TYPE and > PACK_INDEX_EXPR. > * cp-tree.def (PACK_INDEX_TYPE): New. > (PACK_INDEX_EXPR): New. > * cp-tree.h (WILDCARD_TYPE_P): Also check PACK_INDEX_P. > (PACK_INDEX_CHECK): Define. > (PACK_INDEX_P): Define. > (PACK_INDEX_PACK): Define. > (PACK_INDEX_INDEX): Define. > (PACK_INDEX_PARENTHESIZED_P): Define. > (make_pack_index): Declare. > (pack_index_element): Declare. > * cxx-pretty-print.cc (cxx_pretty_printer::expression) <case > PACK_INDEX_EXPR>: New case. > (cxx_pretty_printer::type_id) <case PACK_INDEX_TYPE>: New case. > * error.cc (dump_type) <case PACK_INDEX_TYPE>: New case. > (dump_type_prefix): Handle PACK_INDEX_TYPE. > (dump_type_suffix): Likewise. > (dump_expr) <case PACK_INDEX_EXPR>: New case. > * mangle.cc (write_type) <case PACK_INDEX_TYPE>: New case. > * module.cc (trees_out::type_node) <case PACK_INDEX_TYPE>: New case. > (trees_in::tree_node) <case PACK_INDEX_TYPE>: New case. > * parser.cc (cp_parser_pack_index): New. > (cp_parser_primary_expression): Handle a C++26 pack-index-expression. > (cp_parser_unqualified_id): Handle a C++26 pack-index-specifier. > (cp_parser_nested_name_specifier_opt): See if a pack-index-specifier > follows. Handle a C++26 pack-index-specifier. > (cp_parser_decltype_expr): Set id_expression_or_member_access_p for > pack indexing. > (cp_parser_mem_initializer_id): Handle a C++26 pack-index-specifier. > (cp_parser_simple_type_specifier): Likewise. > (cp_parser_base_specifier): Likewise. > * pt.cc (iterative_hash_template_arg) <case PACK_INDEX_TYPE, > PACK_INDEX_EXPR>: New case. > (find_parameter_packs_r) <case PACK_INDEX_TYPE, PACK_INDEX_EXPR>: New > case. > (make_pack_index): New. > (tsubst_pack_index): New. > (tsubst): Avoid tsubst on PACK_INDEX_TYPE. > <case TYPENAME_TYPE>: Add a call to error. > <case DECLTYPE_TYPE>: Check PACK_INDEX_PARENTHESIZED_P. > <case PACK_INDEX_TYPE>: New case. > (tsubst_expr) <case PACK_INDEX_EXPR>: New case. > (dependent_type_p_r): Return true for PACK_INDEX_TYPE. > (type_dependent_expression_p): Return true for PACK_INDEX_EXPR. > * ptree.cc (cxx_print_type) <case PACK_INDEX_TYPE>: New case. > * semantics.cc (finish_parenthesized_expr): Set > PACK_INDEX_PARENTHESIZED_P for PACK_INDEX_P. > (finish_type_pack_element): Adjust error messages. > (pack_index_element): New. > * tree.cc (cp_tree_equal) <case PACK_INDEX_EXPR>: New case. > (cp_walk_subtrees) <case PACK_INDEX_TYPE, PACK_INDEX_EXPR>: New case. > * typeck.cc (structural_comptypes) <case PACK_INDEX_TYPE>: New case. > > libstdc++-v3/ChangeLog: > > * testsuite/20_util/tuple/element_access/get_neg.cc: Adjust > dg-prune-output. > > gcc/testsuite/ChangeLog: > > * g++.dg/cpp26/pack-indexing1.C: New test. > * g++.dg/cpp26/pack-indexing2.C: New test. > * g++.dg/cpp26/pack-indexing3.C: New test. > * g++.dg/cpp26/pack-indexing4.C: New test. > * g++.dg/cpp26/pack-indexing5.C: New test. > * g++.dg/cpp26/pack-indexing6.C: New test. > * g++.dg/cpp26/pack-indexing7.C: New test. > * g++.dg/cpp26/pack-indexing8.C: New test. > * g++.dg/cpp26/pack-indexing9.C: New test. > * g++.dg/modules/pack-index-1_a.C: New test. > * g++.dg/modules/pack-index-1_b.C: New test. > --- > gcc/cp/constexpr.cc | 5 + > gcc/cp/cp-objcp-common.cc | 2 + > gcc/cp/cp-tree.def | 8 + > gcc/cp/cp-tree.h | 31 +++- > gcc/cp/cxx-pretty-print.cc | 14 ++ > gcc/cp/error.cc | 16 ++ > gcc/cp/mangle.cc | 6 + > gcc/cp/module.cc | 14 ++ > gcc/cp/parser.cc | 147 +++++++++++++++--- > gcc/cp/pt.cc | 101 ++++++++++-- > gcc/cp/ptree.cc | 5 + > gcc/cp/semantics.cc | 22 ++- > gcc/cp/tree.cc | 16 ++ > gcc/cp/typeck.cc | 6 + > gcc/testsuite/g++.dg/cpp26/pack-indexing1.C | 102 ++++++++++++ > gcc/testsuite/g++.dg/cpp26/pack-indexing2.C | 111 +++++++++++++ > gcc/testsuite/g++.dg/cpp26/pack-indexing3.C | 41 +++++ > gcc/testsuite/g++.dg/cpp26/pack-indexing4.C | 65 ++++++++ > gcc/testsuite/g++.dg/cpp26/pack-indexing5.C | 41 +++++ > gcc/testsuite/g++.dg/cpp26/pack-indexing6.C | 51 ++++++ > gcc/testsuite/g++.dg/cpp26/pack-indexing7.C | 16 ++ > gcc/testsuite/g++.dg/cpp26/pack-indexing8.C | 23 +++ > gcc/testsuite/g++.dg/cpp26/pack-indexing9.C | 27 ++++ > gcc/testsuite/g++.dg/modules/pack-index-1_a.C | 18 +++ > gcc/testsuite/g++.dg/modules/pack-index-1_b.C | 15 ++ > .../20_util/tuple/element_access/get_neg.cc | 2 +- > 26 files changed, 867 insertions(+), 38 deletions(-) > create mode 100644 gcc/testsuite/g++.dg/cpp26/pack-indexing1.C > create mode 100644 gcc/testsuite/g++.dg/cpp26/pack-indexing2.C > create mode 100644 gcc/testsuite/g++.dg/cpp26/pack-indexing3.C > create mode 100644 gcc/testsuite/g++.dg/cpp26/pack-indexing4.C > create mode 100644 gcc/testsuite/g++.dg/cpp26/pack-indexing5.C > create mode 100644 gcc/testsuite/g++.dg/cpp26/pack-indexing6.C > create mode 100644 gcc/testsuite/g++.dg/cpp26/pack-indexing7.C > create mode 100644 gcc/testsuite/g++.dg/cpp26/pack-indexing8.C > create mode 100644 gcc/testsuite/g++.dg/cpp26/pack-indexing9.C > create mode 100644 gcc/testsuite/g++.dg/modules/pack-index-1_a.C > create mode 100644 gcc/testsuite/g++.dg/modules/pack-index-1_b.C > > diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc > index c141c0a2844..3c1a10bc13f 100644 > --- a/gcc/cp/constexpr.cc > +++ b/gcc/cp/constexpr.cc > @@ -9939,6 +9939,11 @@ potential_constant_expression_1 (tree t, bool > want_rval, bool strict, bool now, > case EXPR_PACK_EXPANSION: > return RECUR (PACK_EXPANSION_PATTERN (t), want_rval); > > + case PACK_INDEX_EXPR: > + if (!RECUR (PACK_INDEX_PACK (t), want_rval)) > + return false; > + return RECUR (PACK_INDEX_INDEX (t), want_rval); > + > case INDIRECT_REF: > { > tree x = TREE_OPERAND (t, 0); > diff --git a/gcc/cp/cp-objcp-common.cc b/gcc/cp/cp-objcp-common.cc > index 7a0636f1653..fe663ecaeb6 100644 > --- a/gcc/cp/cp-objcp-common.cc > +++ b/gcc/cp/cp-objcp-common.cc > @@ -642,6 +642,7 @@ cp_common_init_ts (void) > MARK_TS_TYPE_NON_COMMON (TEMPLATE_TEMPLATE_PARM); > MARK_TS_TYPE_NON_COMMON (TEMPLATE_TYPE_PARM); > MARK_TS_TYPE_NON_COMMON (TYPE_PACK_EXPANSION); > + MARK_TS_TYPE_NON_COMMON (PACK_INDEX_TYPE); > > /* Statements. */ > MARK_TS_EXP (CLEANUP_STMT); > @@ -700,6 +701,7 @@ cp_common_init_ts (void) > MARK_TS_EXP (NONTYPE_ARGUMENT_PACK); > MARK_TS_EXP (UNARY_LEFT_FOLD_EXPR); > MARK_TS_EXP (UNARY_RIGHT_FOLD_EXPR); > + MARK_TS_EXP (PACK_INDEX_EXPR); > > /* Constraints. */ > MARK_TS_EXP (COMPOUND_REQ); > diff --git a/gcc/cp/cp-tree.def b/gcc/cp/cp-tree.def > index 3de6923f771..fa445ec2781 100644 > --- a/gcc/cp/cp-tree.def > +++ b/gcc/cp/cp-tree.def > @@ -397,6 +397,14 @@ DEFTREECODE (TYPE_PACK_EXPANSION, "type_pack_expansion", > tcc_type, 0) > but will be used for expressions. */ > DEFTREECODE (EXPR_PACK_EXPANSION, "expr_pack_expansion", tcc_expression, 3) > > +/* Represents a pack index Ts...[I], yielding a type. PACK_INDEX_PACK is > + the pack expansion Ts, PACK_INDEX_INDEX the index I. */ > +DEFTREECODE (PACK_INDEX_TYPE, "pack_index_type", tcc_type, 0) > + > +/* Represents a pack index Ts...[I], yielding an expression. PACK_INDEX_PACK > + is the pack expansion Ts, PACK_INDEX_INDEX the index I. */ > +DEFTREECODE (PACK_INDEX_EXPR, "pack_index_expr", tcc_expression, 2) > + > /* Selects the Ith parameter out of an argument pack. This node will > be used when instantiating pack expansions; see > tsubst_pack_expansion. > diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h > index f98a1de42ca..17eccddc1ae 100644 > --- a/gcc/cp/cp-tree.h > +++ b/gcc/cp/cp-tree.h > @@ -451,6 +451,7 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX]; > ATOMIC_CONSTR_MAP_INSTANTIATED_P (in ATOMIC_CONSTR) > contract_semantic (in ASSERTION_, PRECONDITION_, POSTCONDITION_STMT) > RETURN_EXPR_LOCAL_ADDR_P (in RETURN_EXPR) > + PACK_INDEX_PARENTHESIZED_P (in PACK_INDEX_*) > 1: IDENTIFIER_KIND_BIT_1 (in IDENTIFIER_NODE) > TI_PENDING_TEMPLATE_FLAG. > TEMPLATE_PARMS_FOR_INLINE. > @@ -2258,7 +2259,8 @@ enum languages { lang_c, lang_cplusplus }; > || TREE_CODE (T) == BOUND_TEMPLATE_TEMPLATE_PARM \ > || TREE_CODE (T) == DECLTYPE_TYPE \ > || TREE_CODE (T) == TRAIT_TYPE \ > - || TREE_CODE (T) == DEPENDENT_OPERATOR_TYPE) > + || TREE_CODE (T) == DEPENDENT_OPERATOR_TYPE \ > + || PACK_INDEX_P (T)) > > /* Nonzero if T is a class (or struct or union) type. Also nonzero > for template type parameters, typename types, and instantiated > @@ -4001,6 +4003,9 @@ struct GTY(()) lang_decl { > #define PACK_EXPANSION_CHECK(NODE) \ > TREE_CHECK2 (NODE, TYPE_PACK_EXPANSION, EXPR_PACK_EXPANSION) > > +#define PACK_INDEX_CHECK(NODE) \ > + TREE_CHECK2 (NODE, PACK_INDEX_TYPE, PACK_INDEX_EXPR) > + > /* Extracts the type or expression pattern from a TYPE_PACK_EXPANSION or > EXPR_PACK_EXPANSION. */ > #define PACK_EXPANSION_PATTERN(NODE) \ > @@ -4025,6 +4030,22 @@ struct GTY(()) lang_decl { > ? &TYPE_MAX_VALUE_RAW (NODE) \ > : &TREE_OPERAND ((NODE), 2)) > > +/* True if NODE is a pack index. */ > +#define PACK_INDEX_P(NODE) \ > + (TREE_CODE (NODE) == PACK_INDEX_TYPE \ > + || TREE_CODE (NODE) == PACK_INDEX_EXPR) > + > +/* For a pack index T...[N], the pack expansion T. */ > +#define PACK_INDEX_PACK(NODE) \ > + (TREE_CODE (PACK_INDEX_CHECK (NODE)) == PACK_INDEX_TYPE \ > + ? TREE_TYPE (NODE) : TREE_OPERAND (NODE, 0)) > + > +/* For a pack index T...[N], the index N. */ > +#define PACK_INDEX_INDEX(NODE) \ > + *(TREE_CODE (PACK_INDEX_CHECK (NODE)) == PACK_INDEX_TYPE \ > + ? &TYPE_MAX_VALUE_RAW (NODE) \ > + : &TREE_OPERAND ((NODE), 1)) > + > /* True iff this pack expansion is within a function context. */ > #define PACK_EXPANSION_LOCAL_P(NODE) \ > TREE_LANG_FLAG_0 (PACK_EXPANSION_CHECK (NODE)) > @@ -4042,6 +4063,11 @@ struct GTY(()) lang_decl { > #define PACK_EXPANSION_FORCE_EXTRA_ARGS_P(NODE) \ > TREE_LANG_FLAG_3 (PACK_EXPANSION_CHECK (NODE)) > > +/* Indicates whether a pack expansion has been parenthesized. Used for > + a pack expansion in a decltype. */ > +#define PACK_INDEX_PARENTHESIZED_P(NODE) \ > + TREE_LANG_FLAG_1 (PACK_INDEX_CHECK (NODE)) > + > /* True iff the wildcard can match a template parameter pack. */ > #define WILDCARD_PACK_P(NODE) TREE_LANG_FLAG_0 (NODE) > > @@ -7581,6 +7607,7 @@ extern bool template_parameter_pack_p > (const_tree); > extern bool function_parameter_pack_p (const_tree); > extern bool function_parameter_expanded_from_pack_p (tree, tree); > extern tree make_pack_expansion (tree, tsubst_flags_t = > tf_warning_or_error); > +extern tree make_pack_index (tree, tree); > extern bool check_for_bare_parameter_packs (tree, location_t = > UNKNOWN_LOCATION); > extern tree build_template_info (tree, tree); > extern tree get_template_info (const_tree); > @@ -7906,6 +7933,8 @@ extern tree finish_underlying_type (tree); > extern tree calculate_bases (tree, tsubst_flags_t); > extern tree finish_bases (tree, bool); > extern tree calculate_direct_bases (tree, tsubst_flags_t); > +extern tree pack_index_element (tree, tree, bool, > + tsubst_flags_t); > extern tree finish_offsetof (tree, tree, location_t); > extern void finish_decl_cleanup (tree, tree); > extern void finish_eh_cleanup (tree); > diff --git a/gcc/cp/cxx-pretty-print.cc b/gcc/cp/cxx-pretty-print.cc > index f4203123737..15d41b55557 100644 > --- a/gcc/cp/cxx-pretty-print.cc > +++ b/gcc/cp/cxx-pretty-print.cc > @@ -1220,6 +1220,13 @@ cxx_pretty_printer::expression (tree t) > pp_cxx_ws_string (this, "..."); > break; > > + case PACK_INDEX_EXPR: > + expression (PACK_INDEX_PACK (t)); > + pp_cxx_left_bracket (this); > + expression (PACK_INDEX_INDEX (t)); > + pp_cxx_right_bracket (this); > + break; > + > case UNARY_LEFT_FOLD_EXPR: > pp_cxx_unary_left_fold_expression (this, t); > break; > @@ -1920,6 +1927,13 @@ cxx_pretty_printer::type_id (tree t) > pp_cxx_ws_string (this, "..."); > break; > > + case PACK_INDEX_TYPE: > + type_id (PACK_INDEX_PACK (t)); > + pp_cxx_left_bracket (this); > + expression (PACK_INDEX_INDEX (t)); > + pp_cxx_right_bracket (this); > + break; > + > case TYPE_ARGUMENT_PACK: > { > tree args = ARGUMENT_PACK_ARGS (t); > diff --git a/gcc/cp/error.cc b/gcc/cp/error.cc > index 8381f950488..229705d0fa2 100644 > --- a/gcc/cp/error.cc > +++ b/gcc/cp/error.cc > @@ -820,6 +820,13 @@ dump_type (cxx_pretty_printer *pp, tree t, int flags) > pp_cxx_ws_string (pp, "..."); > break; > > + case PACK_INDEX_TYPE: > + dump_type (pp, PACK_INDEX_PACK (t), flags); > + pp_cxx_left_bracket (pp); > + dump_expr (pp, PACK_INDEX_INDEX (t), flags & ~TFF_EXPR_IN_PARENS); > + pp_cxx_right_bracket (pp); > + break; > + > case TYPE_ARGUMENT_PACK: > dump_template_argument (pp, t, flags); > break; > @@ -1094,6 +1101,7 @@ dump_type_prefix (cxx_pretty_printer *pp, tree t, int > flags) > case TYPE_PACK_EXPANSION: > case FIXED_POINT_TYPE: > case NULLPTR_TYPE: > + case PACK_INDEX_TYPE: > dump_type (pp, t, flags); > pp->set_padding (pp_before); > break; > @@ -1226,6 +1234,7 @@ dump_type_suffix (cxx_pretty_printer *pp, tree t, int > flags) > case TYPE_PACK_EXPANSION: > case FIXED_POINT_TYPE: > case NULLPTR_TYPE: > + case PACK_INDEX_TYPE: > break; > > default: > @@ -3151,6 +3160,13 @@ dump_expr (cxx_pretty_printer *pp, tree t, int flags) > pp->expression (t); > break; > > + case PACK_INDEX_EXPR: > + pp->expression (PACK_INDEX_PACK (t)); > + pp_cxx_left_bracket (pp); > + pp->expression (PACK_INDEX_INDEX (t)); > + pp_cxx_right_bracket (pp); > + break; > + > case TRUTH_AND_EXPR: > case TRUTH_OR_EXPR: > case TRUTH_XOR_EXPR: > diff --git a/gcc/cp/mangle.cc b/gcc/cp/mangle.cc > index 4d46c0917a9..c103b50b1f1 100644 > --- a/gcc/cp/mangle.cc > +++ b/gcc/cp/mangle.cc > @@ -2669,6 +2669,12 @@ write_type (tree type) > "use library traits instead", type); > break; > > + case PACK_INDEX_TYPE: > + /* TODO Mangle pack indexing > + <https://github.com/itanium-cxx-abi/cxx-abi/issues/175>. */ > + sorry ("mangling type pack index"); > + break; > + > case LANG_TYPE: > /* fall through. */ > > diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc > index f72e0154dd6..3d69c735a17 100644 > --- a/gcc/cp/module.cc > +++ b/gcc/cp/module.cc > @@ -9209,6 +9209,11 @@ trees_out::type_node (tree type) > tree_node (PACK_EXPANSION_EXTRA_ARGS (type)); > break; > > + case PACK_INDEX_TYPE: > + tree_node (PACK_INDEX_PACK (type)); > + tree_node (PACK_INDEX_INDEX (type)); > + break; > + > case TYPENAME_TYPE: > { > tree_node (TYPE_CONTEXT (type)); > @@ -9776,6 +9781,15 @@ trees_in::tree_node (bool is_use) > } > break; > > + case PACK_INDEX_TYPE: > + { > + tree pack = tree_node (); > + tree index = tree_node (); > + if (!get_overrun ()) > + res = make_pack_index (pack, index); > + } > + break; > + > case TYPENAME_TYPE: > { > tree ctx = tree_node (); > diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc > index c1375ecdbb5..e562ba723ad 100644 > --- a/gcc/cp/parser.cc > +++ b/gcc/cp/parser.cc > @@ -5739,6 +5739,54 @@ cp_parser_fold_expression (cp_parser *parser, tree > expr1) > return finish_binary_fold_expr (loc, expr1, expr2, op); > } > > +/* Parse a pack-index-specifier: > + > + pack-index-specifier: > + typedef-name ... [ constant-expression ] > + > + or a pack-index-expression: > + > + pack-index-expression: > + id-expression ... [ constant-expression ] > + > + PACK is the parsed typedef-name or the id-expression. Returns > + either a PACK_INDEX_TYPE or PACK_INDEX_EXPR. */ > + > +static tree > +cp_parser_pack_index (cp_parser *parser, tree pack) > +{ > + if (cxx_dialect < cxx26) > + pedwarn (cp_lexer_peek_token (parser->lexer)->location, > + OPT_Wc__26_extensions, "pack indexing only available with " > + "%<-std=c++2c%> or %<-std=gnu++2c%>"); > + /* Consume the '...' token. */ > + cp_lexer_consume_token (parser->lexer); > + /* Consume the '['. */ > + cp_lexer_consume_token (parser->lexer); > + > + if (cp_lexer_next_token_is (parser->lexer, CPP_CLOSE_SQUARE)) > + { > + error_at (cp_lexer_peek_token (parser->lexer)->location, > + "pack index missing"); > + cp_lexer_consume_token (parser->lexer); > + return error_mark_node; > + } > + > + tree index = cp_parser_constant_expression (parser, > + /*allow_non_constant_p=*/false, > + /*non_constant_p=*/nullptr, > + /*strict_p=*/true); > + /* Consume the ']'. */ > + cp_parser_require (parser, CPP_CLOSE_SQUARE, RT_CLOSE_SQUARE); > + > + if (TREE_CODE (pack) == TYPE_DECL) > + pack = TREE_TYPE (pack); > + pack = make_pack_expansion (pack); > + if (pack == error_mark_node) > + return error_mark_node; > + return make_pack_index (pack, index); > +} > + > /* Parse a primary-expression. > > primary-expression: > @@ -6368,6 +6416,12 @@ cp_parser_primary_expression (cp_parser *parser, > = make_location (caret_loc, start_loc, finish_loc); > > decl.set_location (combined_loc); > + > + /* "T...[constant-expression]" is a C++26 pack-index-expression. */ > + if (cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS) > + && cp_lexer_nth_token_is (parser->lexer, 2, CPP_OPEN_SQUARE)) > + decl = cp_parser_pack_index (parser, decl); > + > return decl; > } > > @@ -6411,6 +6465,7 @@ missing_template_diag (location_t loc, diagnostic_t > diag_kind = DK_WARNING) > id-expression: > unqualified-id > qualified-id > + pack-index-expression > > qualified-id: > :: [opt] nested-name-specifier template [opt] unqualified-id > @@ -6593,7 +6648,9 @@ cp_parser_id_expression (cp_parser *parser, > identifier > operator-function-id > conversion-function-id > - ~ class-name > + literal-operator-id > + ~ type-name > + ~ computed-type-specifier > template-id > > If TEMPLATE_KEYWORD_P is TRUE, we have just seen the `template' > @@ -6900,6 +6957,14 @@ cp_parser_unqualified_id (cp_parser* parser, > "typedef-name %qD used as destructor declarator", > type_decl); > > + /* "~T...[N]" is a C++26 pack-index-specifier. */ > + if (cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS) > + && cp_lexer_nth_token_is (parser->lexer, 2, CPP_OPEN_SQUARE)) > + { > + type_decl = cp_parser_pack_index (parser, type_decl); > + return build_min_nt_loc (loc, BIT_NOT_EXPR, type_decl); > + } > + > return build_min_nt_loc (loc, BIT_NOT_EXPR, TREE_TYPE (type_decl)); > } > > @@ -6970,9 +7035,10 @@ check_template_keyword_in_nested_name_spec (tree name) > class-or-namespace-name :: nested-name-specifier [opt] > class-or-namespace-name :: template nested-name-specifier [opt] > > - nested-name-specifier: [C++0x] > + nested-name-specifier: [C++11] > type-name :: > namespace-name :: > + computed-type-specifier :: > nested-name-specifier identifier :: > nested-name-specifier template [opt] simple-template-id :: > > @@ -7080,6 +7146,10 @@ cp_parser_nested_name_specifier_opt (cp_parser *parser, > } > > if (token->type != CPP_SCOPE > + /* See if a pack-index-specifier follows. */ > + && !(token->type == CPP_ELLIPSIS > + && cp_lexer_peek_nth_token (parser->lexer, 3)->type > + == CPP_OPEN_SQUARE) > && !cp_parser_nth_token_starts_template_argument_list_p > (parser, 2)) > break; > @@ -7127,6 +7197,12 @@ cp_parser_nested_name_specifier_opt (cp_parser *parser, > check_dependency_p, > type_p, > is_declaration); > + > + /* "T...[constant-expression]" is a C++26 pack-index-specifier. */ > + if (cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS) > + && cp_lexer_nth_token_is (parser->lexer, 2, CPP_OPEN_SQUARE)) > + new_scope = cp_parser_pack_index (parser, new_scope); > + > /* Look for the `::' token. */ > cp_parser_require (parser, CPP_SCOPE, RT_SCOPE); > > @@ -17410,6 +17486,11 @@ cp_parser_decltype_expr (cp_parser *parser, > /* Parse a full expression. */ > expr = cp_parser_expression (parser, /*pidk=*/NULL, /*cast_p=*/false, > /*decltype_p=*/true); > + /* A pack indexing is an id-expression. cp_parser_expression does not > + tell us if the pack indexing was wrapped in (), so we use the > + PACK_INDEX_PARENTHESIZED_P flag to track that. */ > + if (PACK_INDEX_P (expr)) > + id_expression_or_member_access_p = true; > } > > return expr; > @@ -17938,13 +18019,17 @@ cp_parser_mem_initializer (cp_parser* parser) > /* Parse a mem-initializer-id. > > mem-initializer-id: > - :: [opt] nested-name-specifier [opt] class-name > - decltype-specifier (C++11) > + class-or-decltype > identifier > > + class-or-decltype: > + nested-name-specifier [opt] type-name > + nested-name-specifier template simple-template-id > + computed-type-specifier > + > Returns a TYPE indicating the class to be initialized for the first > - production (and the second in C++11). Returns an IDENTIFIER_NODE > - indicating the data member to be initialized for the last production. */ > + production. Returns an IDENTIFIER_NODE indicating the data member to > + be initialized for the second production. */ > > static tree > cp_parser_mem_initializer_id (cp_parser* parser) > @@ -18015,10 +18100,16 @@ cp_parser_mem_initializer_id (cp_parser* parser) > /*class_head_p=*/false, > /*is_declaration=*/true); > /* If we found one, we're done. */ > - if (cp_parser_parse_definitely (parser)) > - return id; > - /* Otherwise, look for an ordinary identifier. */ > - return cp_parser_identifier (parser); > + if (!cp_parser_parse_definitely (parser)) > + /* Otherwise, look for an ordinary identifier. */ > + id = cp_parser_identifier (parser); > + > + /* ": T...[N]" is a C++26 pack-index-specifier. */ > + if (cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS) > + && cp_lexer_nth_token_is (parser->lexer, 2, CPP_OPEN_SQUARE)) > + id = cp_parser_pack_index (parser, id); > + > + return id; > } > > /* Overloading [gram.over] */ > @@ -20436,11 +20527,11 @@ cp_parser_type_specifier (cp_parser* parser, > C++11 Extension: > > simple-type-specifier: > - auto > - decltype ( expression ) > char16_t > char32_t > __underlying_type ( type-id ) > + computed-type-specifier > + placeholder-type-specifier > > C++17 extension: > > @@ -20822,6 +20913,13 @@ cp_parser_simple_type_specifier (cp_parser* parser, > type = NULL_TREE; > } > > + /* "T...[constant-expression]" is a C++26 pack-index-specifier. */ > + if (type > + && type != error_mark_node > + && cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS) > + && cp_lexer_nth_token_is (parser->lexer, 2, CPP_OPEN_SQUARE)) > + type = cp_parser_pack_index (parser, type); > + > if (!type && flag_concepts && decl_specs) > { > /* Try for a type-constraint with template arguments. We check > @@ -29103,12 +29201,21 @@ cp_parser_base_clause (cp_parser* parser) > /* Parse a base-specifier. > > base-specifier: > - attribute-specifier-seq [opt] :: [opt] nested-name-specifier [opt] > - class-name > - attribute-specifier-seq [opt] virtual access-specifier [opt] :: [opt] > - nested-name-specifier [opt] class-name > - attribute-specifier-seq [opt] access-specifier virtual [opt] :: [opt] > - nested-name-specifier [opt] class-name > + attribute-specifier-seq [opt] class-or-decltype > + attribute-specifier-seq [opt] virtual access-specifier [opt] > + class-or-decltype > + attribute-specifier-seq [opt] access-specifier virtual [opt] > + class-or-decltype > + > + class-or-decltype: > + nested-name-specifier [opt] type-name > + nested-name-specifier template simple-template-id > + computed-type-specifier > + > + access-specifier: > + private > + protected > + public > > Returns a TREE_LIST. The TREE_PURPOSE will be one of > ACCESS_{DEFAULT,PUBLIC,PROTECTED,PRIVATE}_[VIRTUAL]_NODE to > @@ -29237,6 +29344,10 @@ cp_parser_base_specifier (cp_parser* parser) > /*class_head_p=*/false, > /*is_declaration=*/true); > type = TREE_TYPE (type); > + /* ": T...[constant-expression]" is a C++26 pack-index-specifier. */ > + if (cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS) > + && cp_lexer_nth_token_is (parser->lexer, 2, CPP_OPEN_SQUARE)) > + type = cp_parser_pack_index (parser, type); > } > > if (type == error_mark_node) > diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc > index 15d6d82f32f..aae8606dd6e 100644 > --- a/gcc/cp/pt.cc > +++ b/gcc/cp/pt.cc > @@ -1789,6 +1789,11 @@ iterative_hash_template_arg (tree arg, hashval_t val) > val = iterative_hash_template_arg (PACK_EXPANSION_PATTERN (arg), val); > return iterative_hash_template_arg (PACK_EXPANSION_EXTRA_ARGS (arg), > val); > > + case PACK_INDEX_TYPE: > + case PACK_INDEX_EXPR: > + val = iterative_hash_template_arg (PACK_INDEX_PACK (arg), val); > + return iterative_hash_template_arg (PACK_INDEX_INDEX (arg), val); > + > case TYPE_ARGUMENT_PACK: > case NONTYPE_ARGUMENT_PACK: > return iterative_hash_template_arg (ARGUMENT_PACK_ARGS (arg), val); > @@ -4031,6 +4036,15 @@ find_parameter_packs_r (tree *tp, int *walk_subtrees, > void* data) > *walk_subtrees = 0; > return NULL_TREE; > > + case PACK_INDEX_TYPE: > + case PACK_INDEX_EXPR: > + /* We can have an expansion of an expansion, such as "Ts...[Is]...", > + so do look into the index. */ > + cp_walk_tree (&PACK_INDEX_INDEX (t), &find_parameter_packs_r, ppd, > + ppd->visited); > + *walk_subtrees = 0; > + return NULL_TREE; > + > case INTEGER_TYPE: > cp_walk_tree (&TYPE_MAX_VALUE (t), &find_parameter_packs_r, > ppd, ppd->visited); > @@ -4257,6 +4271,21 @@ make_pack_expansion (tree arg, tsubst_flags_t complain) > return result; > } > > +/* Create a PACK_INDEX_* using the pack expansion PACK and index INDEX. */ > + > +tree > +make_pack_index (tree pack, tree index) > +{ > + tree t = (TREE_CODE (pack) == TYPE_PACK_EXPANSION > + ? cxx_make_type (PACK_INDEX_TYPE) > + : make_node (PACK_INDEX_EXPR)); > + PACK_INDEX_PACK (t) = pack; > + PACK_INDEX_INDEX (t) = index; > + if (TREE_CODE (t) == PACK_INDEX_TYPE) > + SET_TYPE_STRUCTURAL_EQUALITY (t); > + return t; > +} > + > /* Checks T for any "bare" parameter packs, which have not yet been > expanded, and issues an error if any are found. This operation can > only be done on full expressions or types (e.g., an expression > @@ -13645,11 +13674,12 @@ add_extra_args (tree extra, tree args, > tsubst_flags_t complain, tree in_decl) > return args; > } > > -/* Substitute ARGS into T, which is an pack expansion > - (i.e. TYPE_PACK_EXPANSION or EXPR_PACK_EXPANSION). Returns a > +/* Substitute ARGS into T, which is a pack expansion > + (i.e. TYPE_PACK_EXPANSION or EXPR_PACK_EXPANSION). Returns a > TREE_VEC with the substituted arguments, a PACK_EXPANSION_* node > (if only a partial substitution could be performed) or > ERROR_MARK_NODE if there was an error. */ > + > tree > tsubst_pack_expansion (tree t, tree args, tsubst_flags_t complain, > tree in_decl) > @@ -13950,6 +13980,26 @@ tsubst_pack_expansion (tree t, tree args, > tsubst_flags_t complain, > return result; > } > > +/* Substitute ARGS into T, which is a pack index (i.e., PACK_INDEX_TYPE or > + PACK_INDEX_EXPR). Returns a single type or expression, a PACK_INDEX_* > + node if only a partial substitution could be performed, or ERROR_MARK_NODE > + if there was an error. */ > + > +tree > +tsubst_pack_index (tree t, tree args, tsubst_flags_t complain, tree in_decl) > +{ > + tree index = tsubst_expr (PACK_INDEX_INDEX (t), args, complain, in_decl); > + if (value_dependent_expression_p (index)) > + return t;
In the dependent case I think we want to return a partially instantiated PACK_INDEX_* rather than the original one to correctly handle them inside a generic lambda, e.g.: template<auto... Vs> constexpr auto f() { return []<int N>() { return Vs...[N]; }.template operator()<1>(); } static_assert(f<1, 2, 3>() == 2); template<int N> constexpr auto g() { return []<auto... Vs>() { return Vs...[N]; }.template operator()<1, 2, 3>(); } static_assert(g<1>() == 2); > + tree pack = tsubst_pack_expansion (PACK_INDEX_PACK (t), args, complain, > + in_decl); > + if (TREE_CODE (pack) == TREE_VEC) > + return pack_index_element (index, pack, PACK_INDEX_PARENTHESIZED_P (t), > + complain); > + else > + return t; > +} > + > /* Make an argument pack out of the TREE_VEC VEC. */ > > static tree > @@ -16338,7 +16388,8 @@ tsubst (tree t, tree args, tsubst_flags_t complain, > tree in_decl) > && code != TEMPLATE_PARM_INDEX > && code != IDENTIFIER_NODE > && code != FUNCTION_TYPE > - && code != METHOD_TYPE) > + && code != METHOD_TYPE > + && code != PACK_INDEX_TYPE) > type = tsubst (type, args, complain, in_decl); > if (type == error_mark_node) > return error_mark_node; > @@ -16898,9 +16949,15 @@ tsubst (tree t, tree args, tsubst_flags_t complain, > tree in_decl) > ctx = tsubst_pack_expansion (ctx, args, > complain | tf_qualifying_scope, > in_decl); > - if (ctx == error_mark_node > - || TREE_VEC_LENGTH (ctx) > 1) > + if (ctx == error_mark_node) > return error_mark_node; > + if (TREE_VEC_LENGTH (ctx) > 1) > + { > + if (complain & tf_error) > + error ("%qD expanded to more than one element", > + TYPENAME_TYPE_FULLNAME (t)); > + return error_mark_node; > + } > if (TREE_VEC_LENGTH (ctx) == 0) > { > if (complain & tf_error) > @@ -17042,12 +17099,17 @@ tsubst (tree t, tree args, tsubst_flags_t complain, > tree in_decl) > else > { > bool id = DECLTYPE_TYPE_ID_EXPR_OR_MEMBER_ACCESS_P (t); > - if (id && TREE_CODE (DECLTYPE_TYPE_EXPR (t)) == BIT_NOT_EXPR > - && EXPR_P (type)) > - /* In a template ~id could be either a complement expression > - or an unqualified-id naming a destructor; if instantiating > - it produces an expression, it's not an id-expression or > - member access. */ > + tree op = DECLTYPE_TYPE_EXPR (t); > + if (id > + /* In a template ~id could be either a complement expression > + or an unqualified-id naming a destructor; if instantiating > + it produces an expression, it's not an id-expression or > + member access. */ > + && ((TREE_CODE (op) == BIT_NOT_EXPR && EXPR_P (type)) > + /* If the pack indexing was wrapped in (), it's not an > + id-expression, either. */ > + || (PACK_INDEX_P (op) > + && PACK_INDEX_PARENTHESIZED_P (op)))) > id = false; > type = finish_decltype_type (type, id, complain); > } > @@ -17075,6 +17137,9 @@ tsubst (tree t, tree args, tsubst_flags_t complain, > tree in_decl) > case NONTYPE_ARGUMENT_PACK: > return tsubst_argument_pack (t, args, complain, in_decl); > > + case PACK_INDEX_TYPE: > + return tsubst_pack_index (t, args, complain, in_decl); > + > case VOID_CST: > case INTEGER_CST: > case REAL_CST: > @@ -21776,6 +21841,9 @@ tsubst_expr (tree t, tree args, tsubst_flags_t > complain, tree in_decl) > RETURN (r); > } > > + case PACK_INDEX_EXPR: > + RETURN (tsubst_pack_index (t, args, complain, in_decl)); > + > case EXPR_PACK_EXPANSION: > error ("invalid use of pack expansion expression"); > RETURN (error_mark_node); > @@ -28116,8 +28184,9 @@ dependent_type_p_r (tree type) > } > > /* All TYPE_PACK_EXPANSIONs are dependent, because parameter packs must > - be template parameters. */ > - if (TREE_CODE (type) == TYPE_PACK_EXPANSION) > + be template parameters. This includes pack-index-specifiers. */ > + if (TREE_CODE (type) == TYPE_PACK_EXPANSION > + || TREE_CODE (type) == PACK_INDEX_TYPE) > return true; > > if (TREE_CODE (type) == DEPENDENT_OPERATOR_TYPE) > @@ -28740,8 +28809,10 @@ type_dependent_expression_p (tree expression) > && uses_outer_template_parms_in_constraints (expression)) > return true; > > - /* Always dependent, on the number of arguments if nothing else. */ > - if (TREE_CODE (expression) == EXPR_PACK_EXPANSION) > + /* Always dependent, on the number of arguments if nothing else. This > + includes pack-index-expressions. */ > + if (TREE_CODE (expression) == EXPR_PACK_EXPANSION > + || TREE_CODE (expression) == PACK_INDEX_EXPR) > return true; > > if (TREE_TYPE (expression) == unknown_type_node) > diff --git a/gcc/cp/ptree.cc b/gcc/cp/ptree.cc > index 95c7a69e27c..74152c9476d 100644 > --- a/gcc/cp/ptree.cc > +++ b/gcc/cp/ptree.cc > @@ -193,6 +193,11 @@ cxx_print_type (FILE *file, tree node, int indent) > print_node (file, "args", PACK_EXPANSION_EXTRA_ARGS (node), indent + > 4); > return; > > + case PACK_INDEX_TYPE: > + print_node (file, "pack", PACK_INDEX_PACK (node), indent + 4); > + print_node (file, "index", PACK_INDEX_INDEX (node), indent + 4); > + return; > + > default: > return; > } > diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc > index ab8614e376d..0b7cd514742 100644 > --- a/gcc/cp/semantics.cc > +++ b/gcc/cp/semantics.cc > @@ -2453,6 +2453,8 @@ finish_parenthesized_expr (cp_expr expr) > tree stripped_expr = tree_strip_any_location_wrapper (expr); > if (TREE_CODE (stripped_expr) == STRING_CST) > PAREN_STRING_LITERAL_P (stripped_expr) = 1; > + else if (PACK_INDEX_P (stripped_expr)) > + PACK_INDEX_PARENTHESIZED_P (stripped_expr) = true; > > expr = cp_expr (force_paren_expr (expr), expr.get_location ()); > > @@ -4848,24 +4850,38 @@ finish_type_pack_element (tree idx, tree types, > tsubst_flags_t complain) > if (TREE_CODE (idx) != INTEGER_CST || !INTEGRAL_TYPE_P (TREE_TYPE (idx))) > { > if (complain & tf_error) > - error ("%<__type_pack_element%> index is not an integral constant"); > + error ("pack index is not an integral constant"); > return error_mark_node; > } > if (tree_int_cst_sgn (idx) < 0) > { > if (complain & tf_error) > - error ("%<__type_pack_element%> index is negative"); > + error ("pack index is negative"); > return error_mark_node; > } > if (wi::to_widest (idx) >= TREE_VEC_LENGTH (types)) > { > if (complain & tf_error) > - error ("%<__type_pack_element%> index is out of range"); > + error ("pack index is out of range"); > return error_mark_node; > } > return TREE_VEC_ELT (types, tree_to_shwi (idx)); > } > > +/* In a pack-index T...[N], return the element at index IDX within TYPES. > + PARENTHESIZED_P is true iff the pack index was wrapped in (). */ > + > +tree > +pack_index_element (tree idx, tree types, bool parenthesized_p, > + tsubst_flags_t complain) > +{ > + tree r = finish_type_pack_element (idx, types, complain); > + if (parenthesized_p) > + /* For the benefit of decltype(auto). */ > + r = force_paren_expr (r); > + return r; > +} > + > /* Implement the __direct_bases keyword: Return the direct base classes > of type. */ > > diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc > index 911260685f1..53fcbc7273e 100644 > --- a/gcc/cp/tree.cc > +++ b/gcc/cp/tree.cc > @@ -4245,6 +4245,15 @@ cp_tree_equal (tree t1, tree t2) > return false; > return true; > > + case PACK_INDEX_EXPR: > + if (!cp_tree_equal (PACK_INDEX_PACK (t1), > + PACK_INDEX_PACK (t2))) > + return false; > + if (!cp_tree_equal (PACK_INDEX_INDEX (t1), > + PACK_INDEX_INDEX (t2))) > + return false; > + return true; > + > case COMPONENT_REF: > /* If we're comparing contract conditions of overrides, member > references > compare equal if they designate the same member. */ > @@ -5585,6 +5594,13 @@ cp_walk_subtrees (tree *tp, int *walk_subtrees_p, > walk_tree_fn func, > *walk_subtrees_p = 0; > break; > > + case PACK_INDEX_TYPE: > + case PACK_INDEX_EXPR: > + WALK_SUBTREE (PACK_INDEX_PACK (t)); > + WALK_SUBTREE (PACK_INDEX_INDEX (t)); > + *walk_subtrees_p = 0; > + break; > + > case CAST_EXPR: > case REINTERPRET_CAST_EXPR: > case STATIC_CAST_EXPR: > diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc > index 6ce1bb61fe7..06c632b9ee2 100644 > --- a/gcc/cp/typeck.cc > +++ b/gcc/cp/typeck.cc > @@ -1623,6 +1623,12 @@ structural_comptypes (tree t1, tree t2, int strict) > && comp_template_args (PACK_EXPANSION_EXTRA_ARGS (t1), > PACK_EXPANSION_EXTRA_ARGS (t2))); > > + case PACK_INDEX_TYPE: > + return (cp_tree_equal (PACK_INDEX_PACK (t1), > + PACK_INDEX_PACK (t2)) > + && cp_tree_equal (PACK_INDEX_INDEX (t1), > + PACK_INDEX_INDEX (t2))); > + > case DECLTYPE_TYPE: > if (DECLTYPE_TYPE_ID_EXPR_OR_MEMBER_ACCESS_P (t1) > != DECLTYPE_TYPE_ID_EXPR_OR_MEMBER_ACCESS_P (t2)) > diff --git a/gcc/testsuite/g++.dg/cpp26/pack-indexing1.C > b/gcc/testsuite/g++.dg/cpp26/pack-indexing1.C > new file mode 100644 > index 00000000000..e8c7a90ae58 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/cpp26/pack-indexing1.C > @@ -0,0 +1,102 @@ > +// P2662R3 - Pack Indexing > +// PR c++/113798 > +// { dg-do compile { target c++17 } } > +// { dg-options "" } > + > +template<class, class> struct same_type; > +template<class T> struct same_type<T, T> {}; > + > +template<int I, typename... Ts> > +using Type = Ts...[I]; // { dg-warning "pack indexing only available with" > "" { target c++23_down } } > + > +template<int I, auto... Ts> > +constexpr auto Var = Ts...[I]; // { dg-warning "pack indexing only available > with" "" { target c++23_down } } > + > +template <int I, auto...Ts> > +int > +foo () > +{ > + return Ts...[I]; // { dg-warning "pack indexing only available with" "" { > target c++23_down } } > +} > + > +template<typename... Ts> > +struct S { > + Ts...[0] a; // { dg-warning "pack indexing only available with" "" { > target c++23_down } } > +#if __cpp_concepts >= 201907L > + void foo (auto... Vs) { > + decltype(Vs...[1]) d1 = Vs...[1]; // { dg-warning "pack indexing only > available with" "" { target { c++20 && c++23_down } } } > + } > +#endif > +}; > + > +int > +g () > +{ > + using U = Type<1, char, int, float>; > + using U = int; > + > + constexpr auto V = Var<2, 0, 1, 42>; > + static_assert (V == 42); > + > + U r = foo<2, 0, 1, 42>(); > + > + return r; > +} > + > +void > +fn1 () > +{ > + int i = 0; > + [&i](auto... pack) { > + // type is int > + decltype(pack...[0]) x5 = 42; // { dg-warning "pack indexing only > available with" "" { target c++23_down } } > + // type is int& > + [[maybe_unused]] decltype((pack...[0])) x6 = i; // { dg-warning "pack > indexing only available with" "" { target c++23_down } } > + }(0); > +} > + > +#if __cpp_concepts >= 201907L > +int > +bar (auto... pack) > +{ > + (void) pack...[0]; // { dg-warning "pack indexing only available with" "" > { target { c++20 && c++23_down } } } > + int x = pack...[0]; // { dg-warning "pack indexing only available with" "" > { target { c++20 && c++23_down } } } > + return x; > +} > +#endif > + > +template<auto...pack> > +void > +fn2 () > +{ > + [[maybe_unused]] decltype(pack...[0]) x1; // { dg-warning "pack indexing > only available with" "" { target c++23_down } } > + [[maybe_unused]] decltype((pack...[0])) x2; // { dg-warning "pack indexing > only available with" "" { target c++23_down } } > + same_type<decltype(x1), int>(); > + same_type<decltype(x2), int>(); > +} > + > +template<typename... T> > +void > +fn3 (int p) > +{ > + T...[0] a = p; // { dg-warning "pack indexing only available with" "" { > target c++23_down } } > + (T...[0])(a); // { dg-warning "pack indexing only available with" "" { > target c++23_down } } > +} > + > +template<int... Is> > +void fn4 () > +{ > + same_type<decltype(Is...[0]), int>(); // { dg-warning "pack indexing > only available with" "" { target c++23_down } } > + same_type<decltype((Is...[0])), int>(); // { dg-warning "pack indexing > only available with" "" { target c++23_down } } > +} > + > +void > +g3 () > +{ > + fn2<0>(); > +#if __cpp_concepts >= 201907L > + bar (0); > +#endif > + S<int> s; > + fn4<0, 1, 2>(); > +} > diff --git a/gcc/testsuite/g++.dg/cpp26/pack-indexing2.C > b/gcc/testsuite/g++.dg/cpp26/pack-indexing2.C > new file mode 100644 > index 00000000000..ec32527ed80 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/cpp26/pack-indexing2.C > @@ -0,0 +1,111 @@ > +// P2662R3 - Pack Indexing > +// PR c++/113798 > +// { dg-do compile { target c++26 } } > +// Test invalid cases. > + > +template<int I, typename... Ts> > +using Type = Typo...[I]; // { dg-error "does not name a type" } > + > +template<int I, auto... Ts> > +constexpr auto Var = Typo...[I]; // { dg-error "no parameter packs" } > + > +template<typename... Ts> > +void foo(Ts...[]); // { dg-error "pack index missing" } > + > +template <typename... Ts> > +void f(Ts...[1]); > + > +template<int... N> > +int f2 (X... [N]); // { dg-error "contains no parameter packs" } > + > +struct X; > + > +template<typename T> > +struct TX; > + > +template <typename T, auto V, template<typename> typename Tp> > +void > +bad (int i) > +{ > + i...[0]; // { dg-error "no parameter packs" } > + V...[0]; // { dg-error "no parameter packs" } > + X...[0] x; // { dg-error "no parameter packs" } > + T...[0] t; // { dg-error "no parameter packs" } > + Tp...[0] tp; // { dg-error "expected" } > + > + X...[0] xarr[1]; // { dg-error "no parameter packs" } > + T...[0] tarr[1]; // { dg-error "no parameter packs" } > + Tp...[0] tparr[1]; // { dg-error "expected" } > +} > + > +template<int N> > +int > +getT (auto... Ts) > +{ > + return Ts...[N]; // { dg-error "pack index is out of range" } > +} > + > +template<int N> > +int > +getT2 (auto... Ts) > +{ > + return Ts...[N]; // { dg-error "pack index is negative" } > +} > + > +template<auto N, typename... Ts> > +void > +badtype () > +{ > + Ts...[N] t; // { dg-error "pack index is out of range" } > +} > + > +template<auto N, typename... Ts> > +void > +badtype2 () > +{ > + Ts...[N] t; // { dg-error "pack index is negative" } > +} > + > +int nonconst () { return 42; } > + > +template<typename... Ts> > +void > +badindex () > +{ > + Ts...[nonconst ()] t; // { dg-error "pack index is not an integral > constant" } > + // { dg-error "non-.constexpr. function" "" { target *-*-* } .-1 } > +} > + > +template<typename... Ts> > +struct broken { > + Ts...1; // { dg-error "expected" } > + Ts...[; // { dg-error "invalid" } > + Ts...[1; // { dg-error "invalid" } > + Ts...[]; // { dg-error "pack index missing" } > + > + void foo (auto...Vs) { > + decltype(Vs...[1]) d1 = Vs...[]; // { dg-error "pack index missing" } > + decltype(Vs...[1]) d2 = Vs...[; // { dg-error "expected" } > + } > +}; > + > +int main() > +{ > + // void f<int, double>(int [1], double [1]) > + f<int, double>(nullptr, nullptr); // { dg-error "no matching function" } > + bad<int, 0, TX>(42); > + > + getT<0>(); // { dg-message "required from here" } > + getT<1>(); // { dg-message "required from here" } > + getT2<-1>(); // { dg-message "required from here" } > + > + badtype<0>(); // { dg-message "required from here" } > + badtype<1, int>(); // { dg-message "required from here" } > + badtype2<-1>(); // { dg-message "required from here" } > + badtype2<-1, int>(); // { dg-message "required from here" } > + > + badindex<int, int, int>(); > + > + bool b = nothere...[0]; // { dg-error "no parameter packs" } > + using E = nothere...[0]; // { dg-error "does not name a type" } > +} > diff --git a/gcc/testsuite/g++.dg/cpp26/pack-indexing3.C > b/gcc/testsuite/g++.dg/cpp26/pack-indexing3.C > new file mode 100644 > index 00000000000..8c10b307f3a > --- /dev/null > +++ b/gcc/testsuite/g++.dg/cpp26/pack-indexing3.C > @@ -0,0 +1,41 @@ > +// P2662R3 - Pack Indexing > +// PR c++/113798 > +// { dg-do compile { target c++26 } } > +// From LLVM's cxx2c-pack-indexing.cpp. > + > +template<typename...> > +struct X { }; > + > +template<typename... T> > +requires requires(T...[0]) { {T...[0](0)}; } > +struct S : T...[1] { > + [[maybe_unused]] T...[1] base = {}; > + using foo = T...[1]; > + S() : T...[1]() { } > + X<T...[0]> x; > + const T...[0] f(T...[0]&& parm) noexcept((T...[0])0) { > + T...[0] (*test)(const volatile T...[0]**); > + thread_local T...[0] d; > + [[maybe_unused]] T...[0] a = parm; > + auto ptr = new T...[0](0); > + (*ptr).~T...[0](); > + return T...[0](0); > + typename T...[1]::foo b = 0; > + T...[1]::i = 0; > + return (T...[0])(a); > + new T...[0]; > + [[maybe_unused]] auto l = []<T...[0]>(T...[0][1]) -> T...[0]{ return {}; > }; > + [[maybe_unused]] auto _ = l.template operator()<T...[0]{}>({0}); > + } > + operator T...[0]() const { } > +}; > + > +struct base { > + using foo = int; > + static inline int i = 42; > +}; > + > +int main() > +{ > + S<int, base>().f(0); > +} > diff --git a/gcc/testsuite/g++.dg/cpp26/pack-indexing4.C > b/gcc/testsuite/g++.dg/cpp26/pack-indexing4.C > new file mode 100644 > index 00000000000..8decf3064bc > --- /dev/null > +++ b/gcc/testsuite/g++.dg/cpp26/pack-indexing4.C > @@ -0,0 +1,65 @@ > +// P2662R3 - Pack Indexing > +// PR c++/113798 > +// { dg-do compile { target c++26 } } > +// From LLVM's cxx2c-pack-indexing.cpp. > + > +template <class, class> > +constexpr bool is_same = false; > +template <class T> > +constexpr bool is_same<T, T> = true; > + > +template <typename T> > +constexpr bool > +f (auto&&... p) > +{ > + return is_same<T, decltype(p...[0])>; > +} > + > +void > +g () > +{ > + int a = 0; > + const int b = 0; > + static_assert(f<int&&>(0)); > + static_assert(f<int&>(a)); > + static_assert(f<const int&>(b)); > +} > + > +template<auto... p> > +struct A { > + enum E { > + x = p...[0] > + }; > +}; > +static_assert(A<42>::x == 42); > + > +struct S { }; > +template<auto... p> > +constexpr auto constant_initializer = p...[0]; > +constexpr auto InitOk = constant_initializer<S{}>; > + > +consteval int evaluate(auto... p) { > + return p...[0]; > +} > +constexpr int x = evaluate(42, S{}); > +static_assert(x == 42); > + > +template <auto... Is> > +struct IL{}; > + > +template <typename... Ts> > +struct TL{}; > + > +template <typename Tl, typename Il> > +struct SpliceImpl; > + > +template <typename... Ts, auto... Is> > +struct SpliceImpl<TL<Ts...>, IL<Is...>> > +{ > + using type = TL<Ts...[Is]...>; > +}; > + > +template <typename Tl, typename Il> > +using Splice = typename SpliceImpl<Tl, Il>::type; > +using type = Splice<TL<char, short, long, double>, IL<1, 2>>; > +static_assert(is_same<type, TL<short, long>>); > diff --git a/gcc/testsuite/g++.dg/cpp26/pack-indexing5.C > b/gcc/testsuite/g++.dg/cpp26/pack-indexing5.C > new file mode 100644 > index 00000000000..901956e2dae > --- /dev/null > +++ b/gcc/testsuite/g++.dg/cpp26/pack-indexing5.C > @@ -0,0 +1,41 @@ > +// P2662R3 - Pack Indexing > +// PR c++/113798 > +// { dg-do compile { target c++26 } } > + > +template<class, class> struct same_type; > +template<class T> struct same_type<T, T> {}; > + > +void > +fn1 (auto... Ts) > +{ > + same_type<decltype(Ts...[0]), int>(); > + same_type<decltype((Ts...[0])), int&>(); > + same_type<decltype(Ts...[1]), unsigned int>(); > + same_type<decltype((Ts...[1])), unsigned int&>(); > +} > + > +template<auto... Is> > +void > +fn2 () > +{ > + same_type<decltype(Is...[0]), int>(); > + same_type<decltype((Is...[0])), int>(); > + same_type<decltype(Is...[1]), unsigned int>(); > + same_type<decltype((Is...[1])), unsigned int>(); > + same_type<decltype(Is...[2]), double>(); > + same_type<decltype((Is...[2])), double>(); > + same_type<decltype(Is...[3]), float>(); > + same_type<decltype((Is...[3])), float>(); > + same_type<decltype(Is...[4]), unsigned char>(); > + same_type<decltype((Is...[4])), unsigned char>(); > +} > + > +static constexpr unsigned char c = 'A'; > + > +void > +g () > +{ > + int i = 42; > + fn1 (i, 42u); > + fn2<0, 1u, 2.0, 3.f, c>(); > +} > diff --git a/gcc/testsuite/g++.dg/cpp26/pack-indexing6.C > b/gcc/testsuite/g++.dg/cpp26/pack-indexing6.C > new file mode 100644 > index 00000000000..9af88765254 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/cpp26/pack-indexing6.C > @@ -0,0 +1,51 @@ > +// P2662R3 - Pack Indexing > +// PR c++/113798 > +// { dg-do compile { target c++26 } } > + > +template<class, class> struct same_type; > +template<class T> struct same_type<T, T> {}; > + > +void > +fn1 (auto... Ts) > +{ > + decltype(auto) a1 = Ts...[0]; > + same_type<decltype(a1), int>(); > + decltype(auto) a2 = (Ts...[0]); > + same_type<decltype(a2), int&>(); > +} > + > +template<auto... Is> > +void > +fn2 () > +{ > + decltype(auto) a1 = Is...[0]; > + same_type<decltype(a1), int>(); > + decltype(auto) a2 = (Is...[0]); > + same_type<decltype(a2), int>(); > + decltype(auto) a3 = Is...[1]; > + same_type<decltype(a3), unsigned int>(); > + decltype(auto) a4 = (Is...[1]); > + same_type<decltype(a4), unsigned int>(); > + decltype(auto) a5 = Is...[2]; > + same_type<decltype(a5), double>(); > + decltype(auto) a6 = (Is...[2]); > + same_type<decltype(a6), double>(); > + decltype(auto) a7 = Is...[3]; > + same_type<decltype(a7), float>(); > + decltype(auto) a8 = (Is...[3]); > + same_type<decltype(a8), float>(); > + decltype(auto) a9 = Is...[4]; > + same_type<decltype(a9), unsigned char>(); > + decltype(auto) a10 = (Is...[4]); > + same_type<decltype(a10), unsigned char>(); > +} > + > +static constexpr unsigned char c = 'A'; > + > +void > +g () > +{ > + int i = 42; > + fn1 (i, 42u); > + fn2<0, 1u, 2.0, 3.f, c>(); > +} > diff --git a/gcc/testsuite/g++.dg/cpp26/pack-indexing7.C > b/gcc/testsuite/g++.dg/cpp26/pack-indexing7.C > new file mode 100644 > index 00000000000..06e2a3d0dc7 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/cpp26/pack-indexing7.C > @@ -0,0 +1,16 @@ > +// P2662R3 - Pack Indexing > +// PR c++/113798 > +// { dg-do compile { target c++26 } } > + > +template <int I, auto...Ts> > +decltype(Ts...[I]) > +foo () // { dg-message "sorry, unimplemented: > mangling" } > +{ > + return Ts...[I]; > +} > + > +int > +g () > +{ > + return foo<2, 0, 1, 42>(); > +} > diff --git a/gcc/testsuite/g++.dg/cpp26/pack-indexing8.C > b/gcc/testsuite/g++.dg/cpp26/pack-indexing8.C > new file mode 100644 > index 00000000000..2b1b67c0841 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/cpp26/pack-indexing8.C > @@ -0,0 +1,23 @@ > +// P2662R3 - Pack Indexing > +// PR c++/113798 > +// { dg-do run { target c++26 } } > + > +#include <initializer_list> > + > +template<typename... Ts> > +int > +g (auto... Is) > +{ > + std::initializer_list<Ts...[0]> l{ Is...[0], Is...[1], Is...[2], Is...[3], > Is...[4] }; > + int sum = 0; > + for (auto x : l) > + sum += x; > + return sum; > +} > + > +int > +main () > +{ > + if (g<int> (1, 2, 3, 4, 5) != 15) > + __builtin_abort (); > +} > diff --git a/gcc/testsuite/g++.dg/cpp26/pack-indexing9.C > b/gcc/testsuite/g++.dg/cpp26/pack-indexing9.C > new file mode 100644 > index 00000000000..468ae9dc4d1 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/cpp26/pack-indexing9.C > @@ -0,0 +1,27 @@ > +// P2662R3 - Pack Indexing > +// PR c++/113798 > +// { dg-do compile { target c++26 } } > +// From <https://github.com/itanium-cxx-abi/cxx-abi/issues/175>. > + > +template <class... T> struct tuple { > + template <unsigned I> T...[I] get(); // { dg-message "sorry, > unimplemented: mangling" } > +}; > + > +int > +g () > +{ > + tuple<int> t; > + return t.get<0>(); > +} > + > +template<typename T, typename U> concept C = true; > +template<typename ...T> struct A { > + template<int I, typename ...U> void f(T...[I], U...[I]) requires > C<T...[I], U...[I]>; // { dg-message "sorry, unimplemented: mangling" } > +}; > + > +void > +h () > +{ > + A<char, int, double> a; > + a.f<1, int, int, char>(1, 2); > +} > diff --git a/gcc/testsuite/g++.dg/modules/pack-index-1_a.C > b/gcc/testsuite/g++.dg/modules/pack-index-1_a.C > new file mode 100644 > index 00000000000..30ae189711c > --- /dev/null > +++ b/gcc/testsuite/g++.dg/modules/pack-index-1_a.C > @@ -0,0 +1,18 @@ > +// { dg-module-do run } > +// { dg-additional-options { -std=c++26 -fmodules-ts } } > + > +export module packing1; > +// { dg-module-cmi "packing1" } > + > +export template<int I, typename... Ts> > +using Type = Ts...[I]; > + > +export template<int I, auto... Ts> > +constexpr auto Var = Ts...[I]; > + > +export template <int I, auto...Ts> > +int > +foo () > +{ > + return Ts...[I]; > +} > diff --git a/gcc/testsuite/g++.dg/modules/pack-index-1_b.C > b/gcc/testsuite/g++.dg/modules/pack-index-1_b.C > new file mode 100644 > index 00000000000..dce6f5774d3 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/modules/pack-index-1_b.C > @@ -0,0 +1,15 @@ > +// { dg-additional-options { -std=c++26 -fmodules-ts } } > + > +import packing1; > + > +int > +main () > +{ > + using U = Type<1, char, int, float>; > + using U = int; > + > + U r = foo<2, 0, 1, 42>(); > + > + constexpr auto V = Var<2, 0, 1, 42>; > + static_assert (V == 42); > +} > diff --git a/libstdc++-v3/testsuite/20_util/tuple/element_access/get_neg.cc > b/libstdc++-v3/testsuite/20_util/tuple/element_access/get_neg.cc > index 2346c29b80f..9e002f4d47a 100644 > --- a/libstdc++-v3/testsuite/20_util/tuple/element_access/get_neg.cc > +++ b/libstdc++-v3/testsuite/20_util/tuple/element_access/get_neg.cc > @@ -61,4 +61,4 @@ test03() > > // { dg-error "tuple index must be in range" "" { target *-*-* } 0 } > // { dg-prune-output "no type named 'type' in .*_Nth_type" } > -// { dg-prune-output "'__type_pack_element' index is out of range" } > +// { dg-prune-output "pack index is out of range" } > > base-commit: f003834badbfd9d0c0ad132de8b2f3d550ed120f > -- > 2.47.0 > >