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

Reply via email to