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
>
>