On Tue, 22 Oct 2024, 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>. > > 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
What are the pros and cons of reusing TYPE/EXPR_PACK_EXPANSION instead of creating two new tree codes for these operators (one of whose operands would itself be a bare TYPE/EXPR_PACK_EXPANSION)? I feel a little iffy at first glance about reusing these tree codes since it muddles what "kind" of tree they are: currently they represent a _vector_ or types/exprs (which is reflected by their tcc_exceptional class), and with this approach they can now also represent a single type/expr (despite their tcc_exceptional class), depending on whether PACK_EXPANSION_INDEX is set. At the same time, the pattern of a generic *_PACK_EXPANSION can be anything whereas for these index operators we know it's always a single bare pack, so we also don't need the full expressivity of *_PACK_EXPANSION to represent these operators either. Maybe it's the case that reusing these tree codes significantly simplifies parts of the implementation? > feature is akin to __type_pack_element, so they can share the element > extraction part. > > 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. > > 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. > > PR c++/113798 > > gcc/cp/ChangeLog: > > * cp-tree.def (EXPR_PACK_EXPANSION): Add another operand. > * cp-tree.h (PACK_EXPANSION_INDEX): Define. > (PACK_EXPANSION_PARENTHESIZED_P): Define. > (pack_index_element): Declare. > * cxx-pretty-print.cc (cxx_pretty_printer::expression) > <case EXPR_PACK_EXPANSION>: Print PACK_EXPANSION_INDEX. > (cxx_pretty_printer::type_id) <case TYPE_PACK_EXPANSION>: Print > PACK_EXPANSION_INDEX. > * decl.cc (xref_basetypes): Set PACK_EXPANSION_INDEX. > * error.cc (dump_type): Print PACK_EXPANSION_INDEX. > * mangle.cc (write_type) <case TYPE_PACK_EXPANSION>: New comment. > * module.cc (trees_out::type_node): Stream PACK_EXPANSION_INDEX. > (trees_in::tree_node): Read in PACK_EXPANSION_INDEX. > * parser.cc (cp_parser_pack_index): New. > (cp_parser_primary_expression): Handle a pack-index-expression. > (cp_parser_unqualified_id): Handle a pack-index-specifier. > (cp_parser_nested_name_specifier_opt): Also check for > a pack-index-specifier. Handle a pack-index-specifier. > (cp_parser_mem_initializer_id): Handle a pack-index-specifier. > (cp_parser_simple_type_specifier): Likewise. > (cp_parser_base_specifier): Likewise. > * pt.cc (iterative_hash_template_arg) <case TYPE_PACK_EXPANSION>: Also > hash PACK_EXPANSION_INDEX. > (find_parameter_packs_r) <case VAR_DECL>: A type with > PACK_EXPANSION_INDEX is not a bare parameter pack. > <case TYPE_PACK_EXPANSION>: Walk into PACK_EXPANSION_INDEXes. > (instantiate_class_template): Handle a pack-index-specifier. > (tsubst_pack_expansion): tsubst_expr the PACK_EXPANSION_INDEX. > If there was a PACK_EXPANSION_INDEX, pull out the element via > pack_index_element. > (tsubst) <case TYPENAME_TYPE>: Handle a pack-index-specifier. > <case DECLTYPE_TYPE>: For a PACK_EXPANSION_P, figure out if it should > be treated as an id-expression. > <case TYPE_PACK_EXPANSION>: Handle it if there is a > PACK_EXPANSION_INDEX. > (tsubst_stmt) <case EXPR_PACK_EXPANSION>: Likewise. > (tsubst_expr) <case EXPR_PACK_EXPANSION>: Likewise. > (tsubst_initializer_list): Handle a pack-index-specifier. > * ptree.cc (cxx_print_type) <case TYPE_PACK_EXPANSION>: Print > the PACK_EXPANSION_INDEX. > * semantics.cc (finish_parenthesized_expr): Maybe set > PACK_EXPANSION_PARENTHESIZED_P. > (finish_base_specifier): Check for a PACK_EXPANSION_P with > a PACK_EXPANSION_INDEX. > (get_vec_elt_checking): New, broken out of finish_type_pack_element. > (finish_type_pack_element): Call get_vec_elt_checking. > (pack_index_element): New. > * tree.cc (cp_build_qualified_type): Set PACK_EXPANSION_INDEX. > (cp_tree_equal) <case EXPR_PACK_EXPANSION>: Also compare the > PACK_EXPANSION_INDEXes. > (cp_walk_subtrees) <case TYPE_PACK_EXPANSION, > case EXPR_PACK_EXPANSION>: Walk the PACK_EXPANSION_INDEX. > * typeck.cc (structural_comptypes) <case TYPE_PACK_EXPANSION>: Also > compare the PACK_EXPANSION_INDEXes. > > 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. > --- > gcc/cp/cp-tree.def | 2 +- > gcc/cp/cp-tree.h | 13 ++ > gcc/cp/cxx-pretty-print.cc | 12 ++ > gcc/cp/decl.cc | 8 +- > gcc/cp/error.cc | 6 + > gcc/cp/mangle.cc | 2 + > gcc/cp/module.cc | 3 + > gcc/cp/parser.cc | 144 +++++++++++++++++--- > gcc/cp/pt.cc | 90 +++++++++--- > gcc/cp/ptree.cc | 1 + > gcc/cp/semantics.cc | 50 ++++++- > gcc/cp/tree.cc | 14 +- > gcc/cp/typeck.cc | 4 +- > 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 ++++++ > 18 files changed, 662 insertions(+), 47 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 > > diff --git a/gcc/cp/cp-tree.def b/gcc/cp/cp-tree.def > index 18f75108c7b..576765667d0 100644 > --- a/gcc/cp/cp-tree.def > +++ b/gcc/cp/cp-tree.def > @@ -395,7 +395,7 @@ DEFTREECODE (TYPE_PACK_EXPANSION, "type_pack_expansion", > tcc_type, 0) > > EXPR_PACK_EXPANSION plays precisely the same role as TYPE_PACK_EXPANSION, > but will be used for expressions. */ > -DEFTREECODE (EXPR_PACK_EXPANSION, "expr_pack_expansion", tcc_expression, 3) > +DEFTREECODE (EXPR_PACK_EXPANSION, "expr_pack_expansion", tcc_expression, 4) > > /* Selects the Ith parameter out of an argument pack. This node will > be used when instantiating pack expansions; see > diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h > index a44100a2bc4..12472d95247 100644 > --- a/gcc/cp/cp-tree.h > +++ b/gcc/cp/cp-tree.h > @@ -510,6 +510,7 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX]; > OVL_LOOKUP_P (in OVERLOAD) > LOOKUP_FOUND_P (in RECORD_TYPE, UNION_TYPE, ENUMERAL_TYPE, > NAMESPACE_DECL) > FNDECL_MANIFESTLY_CONST_EVALUATED (in FUNCTION_DECL) > + PACK_EXPANSION_PARENTHESIZED_P (in *_PACK_EXPANSION) > 5: IDENTIFIER_VIRTUAL_P (in IDENTIFIER_NODE) > FUNCTION_RVALUE_QUALIFIED (in FUNCTION_TYPE, METHOD_TYPE) > CALL_EXPR_REVERSE_ARGS (in CALL_EXPR, AGGR_INIT_EXPR) > @@ -4025,6 +4026,12 @@ struct GTY(()) lang_decl { > ? &TYPE_MAX_VALUE_RAW (NODE) \ > : &TREE_OPERAND ((NODE), 2)) > > +/* For a pack-index T...[N], the index N. */ > +#define PACK_EXPANSION_INDEX(NODE) \ > + *(TREE_CODE (PACK_EXPANSION_CHECK (NODE)) == TYPE_PACK_EXPANSION \ > + ? &TYPE_VALUES_RAW (NODE) \ > + : &TREE_OPERAND ((NODE), 3)) > + > /* 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 +4049,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_EXPANSION_PARENTHESIZED_P(NODE) \ > + TREE_LANG_FLAG_4 (PACK_EXPANSION_CHECK (NODE)) > + > /* True iff the wildcard can match a template parameter pack. */ > #define WILDCARD_PACK_P(NODE) TREE_LANG_FLAG_0 (NODE) > > @@ -7906,6 +7918,7 @@ 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, > 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 41e6bdfdda5..47111195f72 100644 > --- a/gcc/cp/cxx-pretty-print.cc > +++ b/gcc/cp/cxx-pretty-print.cc > @@ -1216,6 +1216,12 @@ cxx_pretty_printer::expression (tree t) > case EXPR_PACK_EXPANSION: > expression (PACK_EXPANSION_PATTERN (t)); > pp_cxx_ws_string (this, "..."); > + if (PACK_EXPANSION_INDEX (t)) > + { > + pp_cxx_left_bracket (this); > + expression (PACK_EXPANSION_INDEX (t)); > + pp_cxx_right_bracket (this); > + } > break; > > case UNARY_LEFT_FOLD_EXPR: > @@ -1916,6 +1922,12 @@ cxx_pretty_printer::type_id (tree t) > case TYPE_PACK_EXPANSION: > type_id (PACK_EXPANSION_PATTERN (t)); > pp_cxx_ws_string (this, "..."); > + if (PACK_EXPANSION_INDEX (t)) > + { > + pp_cxx_left_bracket (this); > + expression (PACK_EXPANSION_INDEX (t)); > + pp_cxx_right_bracket (this); > + } > break; > > case TYPE_ARGUMENT_PACK: > diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc > index 7281818be8f..311f4ad6cd1 100644 > --- a/gcc/cp/decl.cc > +++ b/gcc/cp/decl.cc > @@ -17040,8 +17040,12 @@ xref_basetypes (tree ref, tree base_list) > } > > if (PACK_EXPANSION_P (TREE_VALUE (base_list))) > - /* Regenerate the pack expansion for the bases. */ > - basetype = make_pack_expansion (basetype); > + { > + /* Regenerate the pack expansion for the bases. */ > + basetype = make_pack_expansion (basetype); > + PACK_EXPANSION_INDEX (basetype) > + = PACK_EXPANSION_INDEX (TREE_VALUE (base_list)); > + } > > TYPE_MARKED_P (basetype) = 1; > > diff --git a/gcc/cp/error.cc b/gcc/cp/error.cc > index 65f70c595cf..33f964a11bd 100644 > --- a/gcc/cp/error.cc > +++ b/gcc/cp/error.cc > @@ -818,6 +818,12 @@ dump_type (cxx_pretty_printer *pp, tree t, int flags) > case TYPE_PACK_EXPANSION: > dump_type (pp, PACK_EXPANSION_PATTERN (t), flags); > pp_cxx_ws_string (pp, "..."); > + if (PACK_EXPANSION_INDEX (t)) > + { > + pp_cxx_left_bracket (pp); > + dump_expr (pp, PACK_EXPANSION_INDEX (t), flags & ~TFF_EXPR_IN_PARENS); > + pp_cxx_right_bracket (pp); > + } > break; > > case TYPE_ARGUMENT_PACK: > diff --git a/gcc/cp/mangle.cc b/gcc/cp/mangle.cc > index 17988d69e1e..8c8a89897c9 100644 > --- a/gcc/cp/mangle.cc > +++ b/gcc/cp/mangle.cc > @@ -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> */ > break; > > case DECLTYPE_TYPE: > diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc > index fd9b1d3bf2e..da3e2b71a66 100644 > --- a/gcc/cp/module.cc > +++ b/gcc/cp/module.cc > @@ -9207,6 +9207,7 @@ trees_out::type_node (tree type) > u (PACK_EXPANSION_LOCAL_P (type)); > tree_node (PACK_EXPANSION_PARAMETER_PACKS (type)); > tree_node (PACK_EXPANSION_EXTRA_ARGS (type)); > + tree_node (PACK_EXPANSION_INDEX (type)); > break; > > case TYPENAME_TYPE: > @@ -9763,6 +9764,7 @@ trees_in::tree_node (bool is_use) > bool local = u (); > tree param_packs = tree_node (); > tree extra_args = tree_node (); > + tree index = tree_node (); > if (!get_overrun ()) > { > tree expn = cxx_make_type (TYPE_PACK_EXPANSION); > @@ -9771,6 +9773,7 @@ trees_in::tree_node (bool is_use) > PACK_EXPANSION_PARAMETER_PACKS (expn) = param_packs; > PACK_EXPANSION_EXTRA_ARGS (expn) = extra_args; > PACK_EXPANSION_LOCAL_P (expn) = local; > + PACK_EXPANSION_INDEX (expn) = index; > res = expn; > } > } > diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc > index 9d31a975dcf..7a054fb6c05 100644 > --- a/gcc/cp/parser.cc > +++ b/gcc/cp/parser.cc > @@ -5739,6 +5739,56 @@ 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 an EXPR_PACK_EXPANSION or TYPE_PACK_EXPANSION. */ > + > +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 expr = 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; > + PACK_EXPANSION_INDEX (pack) = expr; > + > + return pack; > +} > + > /* Parse a primary-expression. > > primary-expression: > @@ -6368,6 +6418,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 +6467,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 +6650,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 +6959,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 +7037,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 +7148,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 +7199,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); > > @@ -17938,13 +18016,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 +18097,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 +20524,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 +20910,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 +29198,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 +29341,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 b590c32345f..e7f3ce38348 100644 > --- a/gcc/cp/pt.cc > +++ b/gcc/cp/pt.cc > @@ -1786,7 +1786,8 @@ iterative_hash_template_arg (tree arg, hashval_t val) > case TYPE_PACK_EXPANSION: > case EXPR_PACK_EXPANSION: > val = iterative_hash_template_arg (PACK_EXPANSION_PATTERN (arg), val); > - return iterative_hash_template_arg (PACK_EXPANSION_EXTRA_ARGS (arg), > val); > + val = iterative_hash_template_arg (PACK_EXPANSION_EXTRA_ARGS (arg), > val); > + return iterative_hash_template_arg (PACK_EXPANSION_INDEX (arg), val); > > case TYPE_ARGUMENT_PACK: > case NONTYPE_ARGUMENT_PACK: > @@ -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))) > { > /* We don't want to walk into the type of a variadic capture proxy, > because we don't want to see the type parameter pack. */ > @@ -4027,6 +4032,10 @@ find_parameter_packs_r (tree *tp, int *walk_subtrees, > void* data) > > case TYPE_PACK_EXPANSION: > case EXPR_PACK_EXPANSION: > + /* We can have an expansion of an expansion, such as "Ts...[Is]...", > + so do look into the index. */ > + cp_walk_tree (&PACK_EXPANSION_INDEX (t), &find_parameter_packs_r, ppd, > + ppd->visited); > *walk_subtrees = 0; > return NULL_TREE; > > @@ -12524,11 +12533,19 @@ instantiate_class_template (tree type) > > if (PACK_EXPANSION_P (BINFO_TYPE (pbase_binfo))) > { > - expanded_bases = > + expanded_bases = > tsubst_pack_expansion (BINFO_TYPE (pbase_binfo), > args, tf_error, 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. */ > + if (PACK_EXPANSION_INDEX (BINFO_TYPE (pbase_binfo))) > + { > + tree vec = make_tree_vec (1); > + TREE_VEC_ELT (vec, 0) = expanded_bases; > + expanded_bases = vec; > + } > > len = TREE_VEC_LENGTH (expanded_bases); > } > @@ -13669,6 +13686,8 @@ tsubst_pack_expansion (tree t, tree args, > tsubst_flags_t complain, > > levels = TMPL_ARGS_DEPTH (args); > > + tree index = tsubst_expr (PACK_EXPANSION_INDEX (t), args, complain, > in_decl); > + > /* Determine the argument packs that will instantiate the parameter > packs used in the expansion expression. While we're at it, > compute the number of arguments to be expanded and make sure it > @@ -13814,6 +13833,10 @@ tsubst_pack_expansion (tree t, tree args, > tsubst_flags_t complain, > { > tree args = ARGUMENT_PACK_ARGS (TREE_VALUE (packs)); > > + /* C++26 Pack Indexing. */ > + if (index) > + return pack_index_element (index, args, complain); I'd expect every pack index operator to hit this code path since its pattern should always be a bare pack... > + > /* If the argument pack is a single pack expansion, pull it out. */ > if (TREE_VEC_LENGTH (args) == 1 > && pack_expansion_args_count (args)) > @@ -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); ... so this code path should be necessary? > + > return result; > } > > @@ -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; > + if (TREE_VEC_LENGTH (ctx) == 0) > + { > + if (complain & tf_error) > + error ("%qD is instantiated for an empty pack", > + TYPENAME_TYPE_FULLNAME (t)); > + return error_mark_node; > + } > + ctx = TREE_VEC_ELT (ctx, 0); > } > - ctx = TREE_VEC_ELT (ctx, 0); > } > else > ctx = tsubst_entering_scope (ctx, args, > @@ -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)); > type = finish_decltype_type (type, id, complain); > } > return cp_build_qualified_type (type, > @@ -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 (); > + > case VOID_CST: > case INTEGER_CST: > case REAL_CST: > @@ -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); > > @@ -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. */ > + if (PACK_EXPANSION_INDEX (TREE_PURPOSE (t))) > + { > + tree vec = make_tree_vec (1); > + TREE_VEC_ELT (vec, 0) = expanded_bases; > + expanded_bases = vec; > + } > > /* We'll be building separate TREE_LISTs of arguments for > each base. */ > @@ -28115,7 +28172,7 @@ dependent_type_p_r (tree type) > } > > /* All TYPE_PACK_EXPANSIONs are dependent, because parameter packs must > - be template parameters. */ > + be template parameters. This includes pack-index-specifiers. */ > if (TREE_CODE (type) == TYPE_PACK_EXPANSION) > return true; > > @@ -28739,7 +28796,8 @@ 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. */ > + /* Always dependent, on the number of arguments if nothing else. This > + includes pack-index-expressions. */ > if (TREE_CODE (expression) == EXPR_PACK_EXPANSION) > return true; > > @@ -31166,7 +31224,7 @@ do_class_deduction (tree ptype, tree tmpl, tree init, > tree outer_targs, > > /* Return true if INIT is an unparenthesized id-expression or an > unparenthesized class member access. Used for the argument of > - decltype(auto). */ > + decltype(auto), or for C++26 pack indexing. */ > > bool > unparenthesized_id_or_class_member_access_p (tree init) > diff --git a/gcc/cp/ptree.cc b/gcc/cp/ptree.cc > index 15e46752d01..82d08a31b22 100644 > --- a/gcc/cp/ptree.cc > +++ b/gcc/cp/ptree.cc > @@ -190,6 +190,7 @@ cxx_print_type (FILE *file, tree node, int indent) > case TYPE_PACK_EXPANSION: > print_node (file, "pattern", PACK_EXPANSION_PATTERN (node), indent + > 4); > print_node (file, "args", PACK_EXPANSION_EXTRA_ARGS (node), indent + > 4); > + print_node (file, "index", PACK_EXPANSION_INDEX (node), indent + 4); > return; > > default: > diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc > index dabfb1ff53f..7ccd678252c 100644 > --- a/gcc/cp/semantics.cc > +++ b/gcc/cp/semantics.cc > @@ -2452,6 +2452,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_EXPANSION_P (stripped_expr)) > + PACK_EXPANSION_PARENTHESIZED_P (stripped_expr) = true; > > expr = cp_expr (force_paren_expr (expr), expr.get_location ()); > > @@ -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))) > { > error ("%qT is not a class type", base); > result = NULL_TREE; > @@ -4837,34 +4841,66 @@ finish_underlying_type (tree type) > return underlying_type; > } > > -/* 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"); > + } > return error_mark_node; > } > if (tree_int_cst_sgn (idx) < 0) > { > if (complain & tf_error) > - error ("%<__type_pack_element%> index is negative"); > + { > + if (pack_index_p) > + error ("pack index is negative"); > + else > + error ("%<__type_pack_element%> 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"); > + { > + if (pack_index_p) > + error ("pack index is out of range"); > + else > + error ("%<__type_pack_element%> index is out of range"); > + } > return error_mark_node; > } > return TREE_VEC_ELT (types, tree_to_shwi (idx)); > } > > +/* Implement the __type_pack_element keyword: Return the type > + at index IDX within TYPES. */ > + > +static tree > +finish_type_pack_element (tree idx, tree types, tsubst_flags_t complain) > +{ > + return get_vec_elt_checking (idx, types, /*pack_index_p=*/false, complain); > +} > + > +/* In a pack-index T...[N], return the element at index IDX within TYPES. */ > + > +tree > +pack_index_element (tree idx, tree types, tsubst_flags_t complain) > +{ > + return get_vec_elt_checking (idx, types, /*pack_index_p=*/true, complain); > +} > + > /* 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 c80ee068958..8637ab06d77 100644 > --- a/gcc/cp/tree.cc > +++ b/gcc/cp/tree.cc > @@ -1441,7 +1441,10 @@ cp_build_qualified_type (tree type, int type_quals, > tree t = PACK_EXPANSION_PATTERN (type); > > t = cp_build_qualified_type (t, type_quals, complain); > - return make_pack_expansion (t, complain); > + /* Regenerate the pack expansion with a qualified type. */ > + t = make_pack_expansion (t, complain); > + PACK_EXPANSION_INDEX (t) = PACK_EXPANSION_INDEX (type); > + return t; > } > > /* A reference or method type shall not be cv-qualified. > @@ -4242,6 +4245,9 @@ cp_tree_equal (tree t1, tree t2) > if (!comp_template_args (PACK_EXPANSION_EXTRA_ARGS (t1), > PACK_EXPANSION_EXTRA_ARGS (t2))) > return false; > + if (!cp_tree_equal (PACK_EXPANSION_INDEX (t1), > + PACK_EXPANSION_INDEX (t2))) > + return false; > return true; > > case COMPONENT_REF: > @@ -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)); > *walk_subtrees_p = 0; > break; > - > + > case EXPR_PACK_EXPANSION: > WALK_SUBTREE (TREE_OPERAND (t, 0)); > WALK_SUBTREE (PACK_EXPANSION_EXTRA_ARGS (t)); > + if (PACK_EXPANSION_INDEX (t)) > + WALK_SUBTREE (PACK_EXPANSION_INDEX (t)); > *walk_subtrees_p = 0; > break; > > diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc > index 71d879abef1..4f0d1bc8af2 100644 > --- a/gcc/cp/typeck.cc > +++ b/gcc/cp/typeck.cc > @@ -1620,7 +1620,9 @@ structural_comptypes (tree t1, tree t2, int strict) > return (same_type_p (PACK_EXPANSION_PATTERN (t1), > PACK_EXPANSION_PATTERN (t2)) > && comp_template_args (PACK_EXPANSION_EXTRA_ARGS (t1), > - PACK_EXPANSION_EXTRA_ARGS (t2))); > + PACK_EXPANSION_EXTRA_ARGS (t2)) > + && cp_tree_equal (PACK_EXPANSION_INDEX (t1), > + PACK_EXPANSION_INDEX (t2))); > > case DECLTYPE_TYPE: > if (DECLTYPE_TYPE_ID_EXPR_OR_MEMBER_ACCESS_P (t1) > 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..9d72c6582af > --- /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> > +decltype(Ts...[I]) // { dg-warning "pack indexing only available with" "" { > target c++23_down } } > +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..c3c67d6ea34 > --- /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 "cannot convert" } > + 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>(); > +} > > base-commit: 5fd1c0c1b6968d55e3f997d67a4c149edf20c012 > -- > 2.46.2 > >