Ping.
On Wed, Nov 06, 2024 at 03:33:00PM -0500, Marek Polacek wrote:
> On Mon, Nov 04, 2024 at 11:10:05PM -0500, Jason Merrill wrote:
> > On 10/30/24 4:59 PM, Marek Polacek wrote:
> > > On Wed, Oct 30, 2024 at 09:01:36AM -0400, Patrick Palka wrote:
> > > > On Tue, 29 Oct 2024, Marek Polacek wrote:
> > > --- 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))
> >
> > Just PACK_INDEX_TYPE here, I think?
>
> I think so too. Done.
>
> > > /* 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. */
> >
> > "the pack expansion T..."?
>
> Done.
>
> > > +#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))
> >
> > This should only apply to PACK_INDEX_EXPR, I think?
>
> True, fixed.
>
> > > /* 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 7ef79b90868..c62c9d7312e 100644
> > > --- a/gcc/cp/error.cc
> > > +++ b/gcc/cp/error.cc
> > > @@ -814,6 +814,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;
> > > @@ -1088,6 +1095,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;
> > > @@ -1220,6 +1228,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:
> > > @@ -3103,6 +3112,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 9323023ff7f..2ef6d8a6d82 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");
> >
> > Maybe cp_parser_error?
>
> Unsure. This:
>
> template<typename... Ts>
> void foo(Ts...[]);
>
> then generates:
>
> error: variable or field 'foo' declared void
> error: expected primary-expression before '...' token
> error: pack index missing before ']' token
>
> which doesn't seem better.
>
> > > + 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);
> >
> > Shouldn't this be in cp_parser_id_expression?
>
> It should, but I need to wait until after finish_id_expression, so that
> DECL isn't just an identifier node.
>
> > > 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
> >
> > Hmm, seems we never implemented ~decltype.
>
> Looks like CWG 1753: <https://gcc.gnu.org/PR117450>.
>
> > > 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)
> >
> > Also update the comment a few lines up?
>
> Done.
>
> > > /* If the following token is neither a `<' (to begin a
> > > template-id), nor a `::', then we are not looking at a
> > > nested-name-specifier. */
> >
> > > && !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);
> >
> > Shouldn't this be in cp_parser_qualifying_entity?
>
> It could. Moved.
>
> > > /* 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;
> >
> > Since it's an id-expression, I'd think it should have been handled in the
> > earlier call to cp_parser_id_expression.
>
> As mentioned above, I need to wait for the finish_id_expression call.
> But still, I shouldn't have to wait till the cp_parser_expression call;
> we should handle a pack indexing earlier in the function. Changed.
>
> > > 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);
> >
> > Seems like we could replace a lot of this function with a call to
> > cp_parser_class_or_decltype, if it existed. But that doesn't need to happen
> > in this patch.
>
> That would be nice.
>
> > > }
> > > if (type == error_mark_node)
> > > diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
> > > index 15d6d82f32f..6a8d275c4a7 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;
> >
> > Do we need to handle these specifically here? I'd think the handling in
> > cp_walk_subtrees would be enough.
>
> I think I do, otherwise the Ts...[Is]... test doesn't work.
> It is used when calling check_for_bare_parameter_packs.
>
> > > case INTEGER_TYPE:
> > > cp_walk_tree (&TYPE_MAX_VALUE (t), &find_parameter_packs_r,
> > > ppd, ppd->visited);
> > > @@ -4257,6 +4271,33 @@ 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)
> > > +{
> > > + bool for_types;
> > > + if (TREE_CODE (pack) == TYPE_PACK_EXPANSION)
> > > + for_types = true;
> > > + else if (TREE_CODE (pack) == EXPR_PACK_EXPANSION)
> > > + for_types = false;
> > > + else
> > > + {
> > > + /* Maybe we've already partially substituted the pack. */
> > > + gcc_checking_assert (TREE_CODE (pack) == TREE_VEC);
> > > + for_types = TYPE_P (TREE_VEC_ELT (pack, 0));
> > > + }
> > > +
> > > + tree t = (for_types
> > > + ? 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 +13686,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 +13992,25 @@ 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 pack = PACK_INDEX_PACK (t);
> > > + if (PACK_EXPANSION_P (pack))
> > > + pack = tsubst_pack_expansion (pack, args, complain, in_decl);
> > > + tree index = tsubst_expr (PACK_INDEX_INDEX (t), args, complain,
> > > in_decl);
> > > + if (!value_dependent_expression_p (index) && TREE_CODE (pack) ==
> > > TREE_VEC)
> > > + return pack_index_element (index, pack, PACK_INDEX_PARENTHESIZED_P
> > > (t),
> > > + complain);
> > > + else
> > > + return make_pack_index (pack, index);
> > > +}
> > > +
> > > /* Make an argument pack out of the TREE_VEC VEC. */
> > > static tree
> > > @@ -16338,7 +16399,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 +16960,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 +17110,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))))
> >
> > I still think DECLTYPE_TYPE_ID_EXPR_OR_MEMBER_ACCESS_P should be enough in
> > this case; I would think we only need PACK_INDEX_PARENTHESIZED_P for
> > tsubst_pack_index to pass to pack_index_element.
>
> Looks like I don't need this change anymore. Yay!
>
> > > id = false;
> > > type = finish_decltype_type (type, id, complain);
> > > }
> > > @@ -17075,6 +17148,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 +21852,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 +28195,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 +28820,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))
> >
> > ...testsuite...
> >
> > I'm not seeing a test for https://eel.is/c++draft/diff#cpp23.dcl.dcl-2 or
> > the code to handle this case differently in C++23 vs 26.
>
> Ah, right. I've added the test (pack-indexing11.C) but we don't
> compile it C++23 as we should due to:
>
> pack-indexing11.C:7:13: error: expected ',' or '...' before '[' token
> 7 | void f(T... [1]);
> | ^
>
> which seems like a bug. Opened <https://gcc.gnu.org/PR117472>.
>
> Is fixing that a requirement for this patch?
>
> > > 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" }
> >
> > This should be dg-bogus/xfail.
>
> Done.
>
> > > +{
> > > + 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" }
> >
> > dg-bogus/xfail again.
>
> Done.
>
> 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_TYPE.
> (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.
> (cp_parser_qualifying_entity): 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 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_EXPR.
> (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/cpp26/pack-indexing10.C: New test.
> * g++.dg/cpp26/pack-indexing11.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 | 3 +
> 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 | 167 +++++++++++++++---
> gcc/cp/pt.cc | 96 +++++++++-
> 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-indexing10.C | 15 ++
> gcc/testsuite/g++.dg/cpp26/pack-indexing11.C | 13 ++
> 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 +-
> 28 files changed, 906 insertions(+), 40 deletions(-)
> create mode 100644 gcc/testsuite/g++.dg/cpp26/pack-indexing1.C
> create mode 100644 gcc/testsuite/g++.dg/cpp26/pack-indexing10.C
> create mode 100644 gcc/testsuite/g++.dg/cpp26/pack-indexing11.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 71e6dc4ef32..818b9aa93ca 100644
> --- a/gcc/cp/constexpr.cc
> +++ b/gcc/cp/constexpr.cc
> @@ -9947,6 +9947,9 @@ 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:
> + return true;
> +
> 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 92d1dba6a5c..c0b6cd0260a 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 \
> + || TREE_CODE (T) == PACK_INDEX_TYPE)
>
> /* 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 (TREE_CHECK (NODE, PACK_INDEX_EXPR))
> +
> /* 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 7ef79b90868..c62c9d7312e 100644
> --- a/gcc/cp/error.cc
> +++ b/gcc/cp/error.cc
> @@ -814,6 +814,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;
> @@ -1088,6 +1095,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;
> @@ -1220,6 +1228,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:
> @@ -3103,6 +3112,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 4eefb2d3584..f6f87b7d80b 100644
> --- a/gcc/cp/module.cc
> +++ b/gcc/cp/module.cc
> @@ -9240,6 +9240,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));
> @@ -9807,6 +9812,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..082456491a6 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 ::
>
> @@ -7061,8 +7127,8 @@ cp_parser_nested_name_specifier_opt (cp_parser *parser,
> if (token->type != CPP_NAME)
> break;
> /* If the following token is neither a `<' (to begin a
> - template-id), nor a `::', then we are not looking at a
> - nested-name-specifier. */
> + template-id), a `...[' (to begin a pack-index-specifier),
> + nor a `::', then we are not looking at a nested-name-specifier. */
> token = cp_lexer_peek_nth_token (parser->lexer, 2);
>
> if (token->type == CPP_COLON
> @@ -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;
> @@ -7444,6 +7514,13 @@ cp_parser_qualifying_entity (cp_parser *parser,
> is_declaration,
> /*enum_ok=*/cxx_dialect > cxx98);
> successful_parse_p = only_class_p || cp_parser_parse_definitely (parser);
> +
> + /* "T...[constant-expression]" is a C++26 pack-index-specifier. */
> + if (successful_parse_p
> + && cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS)
> + && cp_lexer_nth_token_is (parser->lexer, 2, CPP_OPEN_SQUARE))
> + scope = cp_parser_pack_index (parser, scope);
> +
> /* If that didn't work, try for a namespace-name. */
> if (!only_class_p && !successful_parse_p)
> {
> @@ -17332,6 +17409,12 @@ cp_parser_decltype_expr (cp_parser *parser,
> tree id_expression = expr;
> cp_id_kind idk;
> const char *error_msg;
> + const bool pack_index_p
> + = (cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS)
> + && cp_lexer_nth_token_is (parser->lexer, 2, CPP_OPEN_SQUARE));
> + const bool have_id_expr_p
> + = (pack_index_p
> + || cp_lexer_peek_token (parser->lexer)->type == CPP_CLOSE_PAREN);
>
> if (identifier_p (expr))
> /* Lookup the name we got back from the id-expression. */
> @@ -17343,7 +17426,7 @@ cp_parser_decltype_expr (cp_parser *parser,
> && TREE_CODE (expr) != TYPE_DECL
> && (TREE_CODE (expr) != BIT_NOT_EXPR
> || !TYPE_P (TREE_OPERAND (expr, 0)))
> - && cp_lexer_peek_token (parser->lexer)->type == CPP_CLOSE_PAREN)
> + && have_id_expr_p)
> {
> /* Complete lookup of the id-expression. */
> expr = (finish_id_expression
> @@ -17371,11 +17454,13 @@ cp_parser_decltype_expr (cp_parser *parser,
> }
> }
>
> - if (expr
> - && expr != error_mark_node
> - && cp_lexer_peek_token (parser->lexer)->type == CPP_CLOSE_PAREN)
> - /* We have an id-expression. */
> - id_expression_or_member_access_p = true;
> + if (expr && expr != error_mark_node && have_id_expr_p)
> + {
> + /* We have an id-expression. */
> + id_expression_or_member_access_p = true;
> + if (pack_index_p)
> + expr = cp_parser_pack_index (parser, expr);
> + }
> }
>
> if (!id_expression_or_member_access_p)
> @@ -17938,13 +18023,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 +18104,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 +20531,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 +20917,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 +29205,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 +29348,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 f4213f88b99..e7347c0b730 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,33 @@ 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)
> +{
> + bool for_types;
> + if (TREE_CODE (pack) == TYPE_PACK_EXPANSION)
> + for_types = true;
> + else if (TREE_CODE (pack) == EXPR_PACK_EXPANSION)
> + for_types = false;
> + else
> + {
> + /* Maybe we've already partially substituted the pack. */
> + gcc_checking_assert (TREE_CODE (pack) == TREE_VEC);
> + for_types = TYPE_P (TREE_VEC_ELT (pack, 0));
> + }
> +
> + tree t = (for_types
> + ? 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
> @@ -13648,11 +13689,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)
> @@ -13953,6 +13995,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 pack = PACK_INDEX_PACK (t);
> + if (PACK_EXPANSION_P (pack))
> + pack = tsubst_pack_expansion (pack, args, complain, in_decl);
> + tree index = tsubst_expr (PACK_INDEX_INDEX (t), args, complain, in_decl);
> + const bool parenthesized_p = (TREE_CODE (t) == PACK_INDEX_EXPR
> + && PACK_INDEX_PARENTHESIZED_P (t));
> + if (!value_dependent_expression_p (index) && TREE_CODE (pack) == TREE_VEC)
> + return pack_index_element (index, pack, parenthesized_p, complain);
> + else
> + return make_pack_index (pack, index);
> +}
> +
> /* Make an argument pack out of the TREE_VEC VEC. */
>
> static tree
> @@ -16341,7 +16403,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;
> @@ -16901,9 +16964,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)
> @@ -17078,6 +17147,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:
> @@ -21779,6 +21851,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);
> @@ -28119,8 +28194,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)
> @@ -28743,8 +28819,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..6e80717cde7 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 (TREE_CODE (stripped_expr) == PACK_INDEX_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 8e566cadcaf..73c97c35046 100644
> --- a/gcc/cp/tree.cc
> +++ b/gcc/cp/tree.cc
> @@ -4278,6 +4278,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. */
> @@ -5618,6 +5627,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 4c15e26f692..50c53dad9c5 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-indexing10.C
> b/gcc/testsuite/g++.dg/cpp26/pack-indexing10.C
> new file mode 100644
> index 00000000000..3cd348b3ec6
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp26/pack-indexing10.C
> @@ -0,0 +1,15 @@
> +// P2662R3 - Pack Indexing
> +// PR c++/113798
> +// { dg-do compile { target c++26 } }
> +
> +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);
> diff --git a/gcc/testsuite/g++.dg/cpp26/pack-indexing11.C
> b/gcc/testsuite/g++.dg/cpp26/pack-indexing11.C
> new file mode 100644
> index 00000000000..81e4da8603a
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp26/pack-indexing11.C
> @@ -0,0 +1,13 @@
> +// P2662R3 - Pack Indexing
> +// PR c++/113798
> +// { dg-do compile { target c++26 } }
> +// Test case from [diff.cpp23.dcl.dcl].
> +
> +template <typename... T>
> +void f(T... [1]);
> +template <typename... T>
> +void g(T... ptr[1]);
> +int main() {
> + f<int, double>(nullptr, nullptr); // { dg-error "no matching function"
> }
> + g<int, double>(nullptr, nullptr); // ok
> +}
> 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..de50f7758e3
> --- /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-bogus "sorry, unimplemented:
> mangling" "" { xfail *-*-* } }
> +{
> + 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..afd8a2f9ed6
> --- /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-bogus "sorry, unimplemented:
> mangling" "" { xfail *-*-* } }
> +};
> +
> +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: 345eb9b795d9728733bd0e472529e259ce796ff6
> --
> 2.47.0
>
Marek