[v2] c++: Implement P2662R3, Pack Indexing [PR113798]

Message ID ZyEg2HyDXIJSdsEZ@redhat.com
State New
Headers
Series [v2] c++: Implement P2662R3, Pack Indexing [PR113798] |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_gcc_build--master-arm success Build passed
linaro-tcwg-bot/tcwg_gcc_build--master-aarch64 success Build passed
linaro-tcwg-bot/tcwg_gcc_check--master-arm success Test passed
linaro-tcwg-bot/tcwg_gcc_check--master-aarch64 success Test passed

Commit Message

Marek Polacek Oct. 29, 2024, 5:52 p.m. UTC
  On Tue, Oct 22, 2024 at 07:42:57PM -0400, Jason Merrill wrote:
> On 10/22/24 3:22 PM, Marek Polacek wrote:
> > Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?
> > 
> > -- >8 --
> > This patch implements C++26 Pack Indexing, as described in
> > <https://wg21.link/P2662R3>.
> 
> Great!
> 
> > The issue discussing how to mangle pack indexes has not been resolved
> > yet <https://github.com/itanium-cxx-abi/cxx-abi/issues/175> and I've
> > made no attempt to address it so far.
> > 
> > Rather than introducing a new template code for a pack indexing, I'm
> > adding a new operand to EXPR_PACK_EXPANSION to store the index; for
> > TYPE_PACK_EXPANSION, I'm stashing the index into TYPE_VALUES_RAW.  This
> > feature is akin to __type_pack_element, so they can share the element
> > extraction part.

In v2, I'm using two new codes, as discussed elsewhere.

> > A pack indexing in a decltype proved to be a bit tricky; eventually,
> > I've added PACK_EXPANSION_PARENTHESIZED_P -- while parsing, we can't
> > really tell what it's going to expand to.
> 
> As I comment below, I think we should have enough information while parsing;
> what it expands to doesn't matter.

Yup; I must not have realized that pack-index-expression is a product of
id-expression.
 
> > With this feature, it's valid to write something like
> > 
> >    using U = tmpl<Ts...[Is]...>;
> > 
> > where we first expand the template argument into
> > 
> >    Ts...[Is#0], Ts...[Is#1], ...
> > 
> > and then substitute each individual pack index.
> > 
> > I have no test for the module.cc code, that is just guesswork.
> 
> Looks straightforward enough.

It was.  I made sure with an assert that the new code is exercised.
 
> > @@ -2605,6 +2605,8 @@ write_type (tree type)
> >              case TYPE_PACK_EXPANSION:
> >                write_string ("Dp");
> >                write_type (PACK_EXPANSION_PATTERN (type));
> > +	      /* TODO: Mangle PACK_EXPANSION_INDEX
> > +		 <https://github.com/itanium-cxx-abi/cxx-abi/issues/175>  */
> 
> Could we warn about this so it doesn't get forgotten?  And similarly in
> write_expression?

There is now a new sorry.
 
> > @@ -3952,7 +3953,11 @@ find_parameter_packs_r (tree *tp, int *walk_subtrees, void* data)
> >         break;
> >       case VAR_DECL:
> > -      if (DECL_PACK_P (t))
> > +      /* We can have
> > +	   T...[0] a;
> > +	   (T...[0])(a); // #1
> > +	 where the 'a' in #1 is not a bare parameter pack.  */
> > +      if (DECL_PACK_P (t) && !PACK_EXPANSION_INDEX (TREE_TYPE (t)))
> 
> Seems like the INDEX check should move into DECL_PACK_P?
> 
> Why doesn't this apply to PARM_DECL above?

I think this is now moot.
 
> > @@ -13946,6 +13969,10 @@ tsubst_pack_expansion (tree t, tree args, tsubst_flags_t complain,
> >         && PACK_EXPANSION_P (TREE_VEC_ELT (result, 0)))
> >       return TREE_VEC_ELT (result, 0);
> > +  /* C++26 Pack Indexing.  */
> > +  if (index)
> > +    return pack_index_element (index, result, complain);
> 
> Could we only compute the desired element rather than computing all of them
> and selecting the desired one?

I don't think so.  Especially now that the PACK_EXPANSION_P is just one
operand of a PACK_INDEX_*, and tsubst_pack_expansion is agnostic about
whether the expansion is part of a pack index.
 
> > @@ -16897,17 +16924,23 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
> >   	    ctx = tsubst_pack_expansion (ctx, args,
> >   					 complain | tf_qualifying_scope,
> >   					 in_decl);
> > -	    if (ctx == error_mark_node
> > -		|| TREE_VEC_LENGTH (ctx) > 1)
> > +	    if (ctx == error_mark_node)
> >   	      return error_mark_node;
> > -	    if (TREE_VEC_LENGTH (ctx) == 0)
> > +	    /* If there was a pack-index-specifier, we won't get a TREE_VEC,
> > +	       just the single element.  */
> > +	    if (TREE_CODE (ctx) == TREE_VEC)
> >   	      {
> > -		if (complain & tf_error)
> > -		  error ("%qD is instantiated for an empty pack",
> > -			 TYPENAME_TYPE_FULLNAME (t));
> > -		return error_mark_node;
> > +		if (TREE_VEC_LENGTH (ctx) > 1)
> > +		  return error_mark_node;
> 
> This is preexisting, but it seems like we're missing a call to error() in
> this case.

Added.
 
> > @@ -17041,13 +17074,20 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
> >   	else
> >   	  {
> >   	    bool id = DECLTYPE_TYPE_ID_EXPR_OR_MEMBER_ACCESS_P (t);
> > -	    if (id && TREE_CODE (DECLTYPE_TYPE_EXPR (t)) == BIT_NOT_EXPR
> > -		&& EXPR_P (type))
> > +	    tree op = DECLTYPE_TYPE_EXPR (t);
> > +	    if (id && TREE_CODE (op) == BIT_NOT_EXPR && EXPR_P (type))
> >   	      /* In a template ~id could be either a complement expression
> >   		 or an unqualified-id naming a destructor; if instantiating
> >   		 it produces an expression, it's not an id-expression or
> >   		 member access.  */
> >   	      id = false;
> > +	    /* With pack indexing, we don't know what it's going to expand to
> > +	       until instantiation.  The intent is that a pack indexing
> > +	       expression behaves exactly as the underlying expression
> > +	       would.  */
> > +	    else if (PACK_EXPANSION_P (op))
> > +	      id = (!PACK_EXPANSION_PARENTHESIZED_P (op)
> > +		    && unparenthesized_id_or_class_member_access_p (type));
> 
> Why isn't the value of DECLTYPE_TYPE_ID_EXPR_OR_MEMBER_ACCESS_P already
> accurate in this case?  It seems to me that 'id' should be based on the
> syntax of the decltype, not what the substitution produces.
> 
> And so maybe we don't need PACK_EXPANSION_PARENTHESIZED_P except maybe for
> decltype(auto)?

I think this should be resolved.  I think I need PACK_INDEX_PARENTHESIZED_P
for both decltype and decltype(auto).  Previously I wasn't testing
decltype(auto) at all, so I've added new test.
 
> > @@ -17074,6 +17114,11 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
> >       case NONTYPE_ARGUMENT_PACK:
> >         return tsubst_argument_pack (t, args, complain, in_decl);
> > +    case TYPE_PACK_EXPANSION:
> > +      if (PACK_EXPANSION_INDEX (t))
> > +	return tsubst_pack_expansion (t, args, complain, in_decl);
> > +      gcc_fallthrough ();
> 
> Let's gcc_unreachable here rather than fallthrough to unreachable.

Done.
 
> > @@ -19596,6 +19641,8 @@ tsubst_stmt (tree t, tree args, tsubst_flags_t complain, tree in_decl)
> >         }
> >       case EXPR_PACK_EXPANSION:
> > +      if (PACK_EXPANSION_INDEX (t))
> > +	RETURN (tsubst_pack_expansion (t, args, complain, in_decl));
> >         error ("invalid use of pack expansion expression");
> >         RETURN (error_mark_node);
> > @@ -21776,6 +21823,8 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl)
> >         }
> >       case EXPR_PACK_EXPANSION:
> > +      if (PACK_EXPANSION_INDEX (t))
> > +	RETURN (tsubst_pack_expansion (t, args, complain, in_decl));
> >         error ("invalid use of pack expansion expression");
> >         RETURN (error_mark_node);
> 
> Preexisting, but do we need this case in both _stmt and _expr?

Looks like I don't need the _stmt case; dropped.
 
> > @@ -27685,6 +27734,14 @@ tsubst_initializer_list (tree t, tree argvec)
> >                                                    NULL_TREE);
> >             if (expanded_bases == error_mark_node)
> >               continue;
> > +	  /* If there was a pack-index-specifier, we won't get
> > +	     a TREE_VEC but the rest of the code assumes so.  */
> 
> Hmm, this difference in return value seems fragile, but I guess you only
> actually need to compensate for it in a couple of places...

This doesn't happen anymore due to the new codes.
 
> > @@ -4181,7 +4183,9 @@ finish_base_specifier (tree base, tree access, bool virtual_p)
> >         error ("invalid base-class specification");
> >         result = NULL_TREE;
> >       }
> > -  else if (! MAYBE_CLASS_TYPE_P (base))
> > +  else if (! MAYBE_CLASS_TYPE_P (base)
> > +	   && ! (PACK_EXPANSION_P (base)
> > +		 && PACK_EXPANSION_INDEX (base)))
> 
> Instead of this change, this case should be added to WILDCARD_TYPE_P.

Done.
 
> > -/* Implement the __type_pack_element keyword: Return the type
> > -   at index IDX within TYPES.  */
> > +/* Return the type at index IDX within TYPES.  */
> >   static tree
> > -finish_type_pack_element (tree idx, tree types, tsubst_flags_t complain)
> > +get_vec_elt_checking (tree idx, tree types, bool pack_index_p,
> > +		      tsubst_flags_t complain)
> >   {
> >     idx = maybe_constant_value (idx);
> >     if (TREE_CODE (idx) != INTEGER_CST || !INTEGRAL_TYPE_P (TREE_TYPE (idx)))
> >       {
> >         if (complain & tf_error)
> > -	error ("%<__type_pack_element%> index is not an integral constant");
> > +	{
> > +	  if (pack_index_p)
> > +	    error ("pack index is not an integral constant");
> > +	  else
> > +	    error ("%<__type_pack_element%> index is not an integral constant");
> 
> Maybe just always say "pack index"?

Yeah.  Done.
 
> > @@ -5575,12 +5581,16 @@ cp_walk_subtrees (tree *tp, int *walk_subtrees_p, walk_tree_fn func,
> >       case TYPE_PACK_EXPANSION:
> >         WALK_SUBTREE (TREE_TYPE (t));
> >         WALK_SUBTREE (PACK_EXPANSION_EXTRA_ARGS (t));
> > +      if (PACK_EXPANSION_INDEX (t))
> > +	WALK_SUBTREE (PACK_EXPANSION_INDEX (t));
> 
> I don't think it's necessary to check non-null before WALK_SUBTREE.

Fixed.

I've added a number of tests.  I've not handled the mangling part, though.

Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?

-- >8 --
This patch implements C++26 Pack Indexing, as described in
<https://wg21.link/P2662R3>.

The issue discussing how to mangle pack indexes has not been resolved
yet <https://github.com/itanium-cxx-abi/cxx-abi/issues/175> and I've
made no attempt to address it so far.

Unlike v1, which used augmented TYPE/EXPR_PACK_EXPANSION codes, this
version introduces two new codes: PACK_INDEX_EXPR and PACK_INDEX_TYPE.
Both carry two operands: the pack expansion and the index.  They are
handled in tsubst_pack_index: substitute the index and the pack and
then extract the element from the vector (if possible).

To handle pack indexing in a decltype or with decltype(auto), there is
also the new PACK_INDEX_PARENTHESIZED_P flag.

With this feature, it's valid to write something like

  using U = tmpl<Ts...[Is]...>;

where we first expand the template argument into

  Ts...[Is#0], Ts...[Is#1], ...

and then substitute each individual pack index.

	PR c++/113798

gcc/cp/ChangeLog:

	* constexpr.cc (potential_constant_expression_1) <case PACK_INDEX_EXPR>:
	New case.
	* cp-objcp-common.cc (cp_common_init_ts): Mark PACK_INDEX_TYPE and
	PACK_INDEX_EXPR.
	* cp-tree.def (PACK_INDEX_TYPE): New.
	(PACK_INDEX_EXPR): New.
	* cp-tree.h (WILDCARD_TYPE_P): Also check PACK_INDEX_P.
	(PACK_INDEX_CHECK): Define.
	(PACK_INDEX_P): Define.
	(PACK_INDEX_PACK): Define.
	(PACK_INDEX_INDEX): Define.
	(PACK_INDEX_PARENTHESIZED_P): Define.
	(make_pack_index): Declare.
	(pack_index_element): Declare.
	* cxx-pretty-print.cc (cxx_pretty_printer::expression) <case
	PACK_INDEX_EXPR>: New case.
	(cxx_pretty_printer::type_id) <case PACK_INDEX_TYPE>: New case.
	* error.cc (dump_type) <case PACK_INDEX_TYPE>: New case.
	(dump_type_prefix): Handle PACK_INDEX_TYPE.
	(dump_type_suffix): Likewise.
	(dump_expr) <case PACK_INDEX_EXPR>: New case.
	* mangle.cc (write_type) <case PACK_INDEX_TYPE>: New case.
	* module.cc (trees_out::type_node) <case PACK_INDEX_TYPE>: New case.
	(trees_in::tree_node) <case PACK_INDEX_TYPE>: New case.
	* parser.cc (cp_parser_pack_index): New.
	(cp_parser_primary_expression): Handle a C++26 pack-index-expression.
	(cp_parser_unqualified_id): Handle a C++26 pack-index-specifier.
	(cp_parser_nested_name_specifier_opt): See if a pack-index-specifier
	follows.  Handle a C++26 pack-index-specifier.
	(cp_parser_decltype_expr): Set id_expression_or_member_access_p for
	pack indexing.
	(cp_parser_mem_initializer_id): Handle a C++26 pack-index-specifier.
	(cp_parser_simple_type_specifier): Likewise.
	(cp_parser_base_specifier): Likewise.
	* pt.cc (iterative_hash_template_arg) <case PACK_INDEX_TYPE,
	PACK_INDEX_EXPR>: New case.
	(find_parameter_packs_r) <case PACK_INDEX_TYPE, PACK_INDEX_EXPR>: New
	case.
	(make_pack_index): New.
	(tsubst_pack_index): New.
	(tsubst): Avoid tsubst on PACK_INDEX_TYPE.
	<case TYPENAME_TYPE>: Add a call to error.
	<case DECLTYPE_TYPE>: Check PACK_INDEX_PARENTHESIZED_P.
	<case PACK_INDEX_TYPE>: New case.
	(tsubst_expr) <case PACK_INDEX_EXPR>: New case.
	(dependent_type_p_r): Return true for PACK_INDEX_TYPE.
	(type_dependent_expression_p): Return true for PACK_INDEX_EXPR.
	* ptree.cc (cxx_print_type) <case PACK_INDEX_TYPE>: New case.
	* semantics.cc (finish_parenthesized_expr): Set
	PACK_INDEX_PARENTHESIZED_P for PACK_INDEX_P.
	(finish_type_pack_element): Adjust error messages.
	(pack_index_element): New.
	* tree.cc (cp_tree_equal) <case PACK_INDEX_EXPR>: New case.
	(cp_walk_subtrees) <case PACK_INDEX_TYPE, PACK_INDEX_EXPR>: New case.
	* typeck.cc (structural_comptypes) <case PACK_INDEX_TYPE>: New case.

libstdc++-v3/ChangeLog:

	* testsuite/20_util/tuple/element_access/get_neg.cc: Adjust
	dg-prune-output.

gcc/testsuite/ChangeLog:

	* g++.dg/cpp26/pack-indexing1.C: New test.
	* g++.dg/cpp26/pack-indexing2.C: New test.
	* g++.dg/cpp26/pack-indexing3.C: New test.
	* g++.dg/cpp26/pack-indexing4.C: New test.
	* g++.dg/cpp26/pack-indexing5.C: New test.
	* g++.dg/cpp26/pack-indexing6.C: New test.
	* g++.dg/cpp26/pack-indexing7.C: New test.
	* g++.dg/cpp26/pack-indexing8.C: New test.
	* g++.dg/cpp26/pack-indexing9.C: New test.
	* g++.dg/modules/pack-index-1_a.C: New test.
	* g++.dg/modules/pack-index-1_b.C: New test.
---
 gcc/cp/constexpr.cc                           |   5 +
 gcc/cp/cp-objcp-common.cc                     |   2 +
 gcc/cp/cp-tree.def                            |   8 +
 gcc/cp/cp-tree.h                              |  31 +++-
 gcc/cp/cxx-pretty-print.cc                    |  14 ++
 gcc/cp/error.cc                               |  16 ++
 gcc/cp/mangle.cc                              |   6 +
 gcc/cp/module.cc                              |  14 ++
 gcc/cp/parser.cc                              | 147 +++++++++++++++---
 gcc/cp/pt.cc                                  | 101 ++++++++++--
 gcc/cp/ptree.cc                               |   5 +
 gcc/cp/semantics.cc                           |  22 ++-
 gcc/cp/tree.cc                                |  16 ++
 gcc/cp/typeck.cc                              |   6 +
 gcc/testsuite/g++.dg/cpp26/pack-indexing1.C   | 102 ++++++++++++
 gcc/testsuite/g++.dg/cpp26/pack-indexing2.C   | 111 +++++++++++++
 gcc/testsuite/g++.dg/cpp26/pack-indexing3.C   |  41 +++++
 gcc/testsuite/g++.dg/cpp26/pack-indexing4.C   |  65 ++++++++
 gcc/testsuite/g++.dg/cpp26/pack-indexing5.C   |  41 +++++
 gcc/testsuite/g++.dg/cpp26/pack-indexing6.C   |  51 ++++++
 gcc/testsuite/g++.dg/cpp26/pack-indexing7.C   |  16 ++
 gcc/testsuite/g++.dg/cpp26/pack-indexing8.C   |  23 +++
 gcc/testsuite/g++.dg/cpp26/pack-indexing9.C   |  27 ++++
 gcc/testsuite/g++.dg/modules/pack-index-1_a.C |  18 +++
 gcc/testsuite/g++.dg/modules/pack-index-1_b.C |  15 ++
 .../20_util/tuple/element_access/get_neg.cc   |   2 +-
 26 files changed, 867 insertions(+), 38 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp26/pack-indexing1.C
 create mode 100644 gcc/testsuite/g++.dg/cpp26/pack-indexing2.C
 create mode 100644 gcc/testsuite/g++.dg/cpp26/pack-indexing3.C
 create mode 100644 gcc/testsuite/g++.dg/cpp26/pack-indexing4.C
 create mode 100644 gcc/testsuite/g++.dg/cpp26/pack-indexing5.C
 create mode 100644 gcc/testsuite/g++.dg/cpp26/pack-indexing6.C
 create mode 100644 gcc/testsuite/g++.dg/cpp26/pack-indexing7.C
 create mode 100644 gcc/testsuite/g++.dg/cpp26/pack-indexing8.C
 create mode 100644 gcc/testsuite/g++.dg/cpp26/pack-indexing9.C
 create mode 100644 gcc/testsuite/g++.dg/modules/pack-index-1_a.C
 create mode 100644 gcc/testsuite/g++.dg/modules/pack-index-1_b.C


base-commit: f003834badbfd9d0c0ad132de8b2f3d550ed120f
  

Comments

Patrick Palka Oct. 30, 2024, 1:01 p.m. UTC | #1
On Tue, 29 Oct 2024, Marek Polacek wrote:

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

In the dependent case I think we want to return a partially instantiated
PACK_INDEX_* rather than the original one to correctly handle them inside
a generic lambda, e.g.:

  template<auto... Vs>
  constexpr auto f() {
    return []<int N>() { return Vs...[N]; }.template operator()<1>();
  }
  static_assert(f<1, 2, 3>() == 2);

  template<int N>
  constexpr auto g() {
    return []<auto... Vs>() { return Vs...[N]; }.template operator()<1, 2, 3>();
  }
  static_assert(g<1>() == 2);

> +  tree pack = tsubst_pack_expansion (PACK_INDEX_PACK (t), args, complain,
> +				     in_decl);
> +  if (TREE_CODE (pack) == TREE_VEC)
> +   return pack_index_element (index, pack, PACK_INDEX_PARENTHESIZED_P (t),
> +			      complain);
> +  else
> +    return t;
> +}
> +
>  /* Make an argument pack out of the TREE_VEC VEC.  */
>  
>  static tree
> @@ -16338,7 +16388,8 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
>        && code != TEMPLATE_PARM_INDEX
>        && code != IDENTIFIER_NODE
>        && code != FUNCTION_TYPE
> -      && code != METHOD_TYPE)
> +      && code != METHOD_TYPE
> +      && code != PACK_INDEX_TYPE)
>      type = tsubst (type, args, complain, in_decl);
>    if (type == error_mark_node)
>      return error_mark_node;
> @@ -16898,9 +16949,15 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
>  	    ctx = tsubst_pack_expansion (ctx, args,
>  					 complain | tf_qualifying_scope,
>  					 in_decl);
> -	    if (ctx == error_mark_node
> -		|| TREE_VEC_LENGTH (ctx) > 1)
> +	    if (ctx == error_mark_node)
>  	      return error_mark_node;
> +	    if (TREE_VEC_LENGTH (ctx) > 1)
> +		{
> +		  if (complain & tf_error)
> +		    error ("%qD expanded to more than one element",
> +			   TYPENAME_TYPE_FULLNAME (t));
> +		  return error_mark_node;
> +		}
>  	    if (TREE_VEC_LENGTH (ctx) == 0)
>  	      {
>  		if (complain & tf_error)
> @@ -17042,12 +17099,17 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
>  	else
>  	  {
>  	    bool id = DECLTYPE_TYPE_ID_EXPR_OR_MEMBER_ACCESS_P (t);
> -	    if (id && TREE_CODE (DECLTYPE_TYPE_EXPR (t)) == BIT_NOT_EXPR
> -		&& EXPR_P (type))
> -	      /* In a template ~id could be either a complement expression
> -		 or an unqualified-id naming a destructor; if instantiating
> -		 it produces an expression, it's not an id-expression or
> -		 member access.  */
> +	    tree op = DECLTYPE_TYPE_EXPR (t);
> +	    if (id
> +		/* In a template ~id could be either a complement expression
> +		   or an unqualified-id naming a destructor; if instantiating
> +		   it produces an expression, it's not an id-expression or
> +		   member access.  */
> +		&& ((TREE_CODE (op) == BIT_NOT_EXPR && EXPR_P (type))
> +		    /* If the pack indexing was wrapped in (), it's not an
> +		       id-expression, either.  */
> +		    || (PACK_INDEX_P (op)
> +			&& PACK_INDEX_PARENTHESIZED_P (op))))
>  	      id = false;
>  	    type = finish_decltype_type (type, id, complain);
>  	  }
> @@ -17075,6 +17137,9 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
>      case NONTYPE_ARGUMENT_PACK:
>        return tsubst_argument_pack (t, args, complain, in_decl);
>  
> +    case PACK_INDEX_TYPE:
> +      return tsubst_pack_index (t, args, complain, in_decl);
> +
>      case VOID_CST:
>      case INTEGER_CST:
>      case REAL_CST:
> @@ -21776,6 +21841,9 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl)
>  	RETURN (r);
>        }
>  
> +    case PACK_INDEX_EXPR:
> +    RETURN (tsubst_pack_index (t, args, complain, in_decl));
> +
>      case EXPR_PACK_EXPANSION:
>        error ("invalid use of pack expansion expression");
>        RETURN (error_mark_node);
> @@ -28116,8 +28184,9 @@ dependent_type_p_r (tree type)
>      }
>  
>    /* All TYPE_PACK_EXPANSIONs are dependent, because parameter packs must
> -     be template parameters.  */
> -  if (TREE_CODE (type) == TYPE_PACK_EXPANSION)
> +     be template parameters.  This includes pack-index-specifiers.  */
> +  if (TREE_CODE (type) == TYPE_PACK_EXPANSION
> +      || TREE_CODE (type) == PACK_INDEX_TYPE)
>      return true;
>  
>    if (TREE_CODE (type) == DEPENDENT_OPERATOR_TYPE)
> @@ -28740,8 +28809,10 @@ type_dependent_expression_p (tree expression)
>        && uses_outer_template_parms_in_constraints (expression))
>      return true;
>  
> -  /* Always dependent, on the number of arguments if nothing else.  */
> -  if (TREE_CODE (expression) == EXPR_PACK_EXPANSION)
> +  /* Always dependent, on the number of arguments if nothing else.  This
> +     includes pack-index-expressions.  */
> +  if (TREE_CODE (expression) == EXPR_PACK_EXPANSION
> +      || TREE_CODE (expression) == PACK_INDEX_EXPR)
>      return true;
>  
>    if (TREE_TYPE (expression) == unknown_type_node)
> diff --git a/gcc/cp/ptree.cc b/gcc/cp/ptree.cc
> index 95c7a69e27c..74152c9476d 100644
> --- a/gcc/cp/ptree.cc
> +++ b/gcc/cp/ptree.cc
> @@ -193,6 +193,11 @@ cxx_print_type (FILE *file, tree node, int indent)
>        print_node (file, "args", PACK_EXPANSION_EXTRA_ARGS (node), indent + 4);
>        return;
>  
> +    case PACK_INDEX_TYPE:
> +      print_node (file, "pack", PACK_INDEX_PACK (node), indent + 4);
> +      print_node (file, "index", PACK_INDEX_INDEX (node), indent + 4);
> +      return;
> +
>      default:
>        return;
>      }
> diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
> index ab8614e376d..0b7cd514742 100644
> --- a/gcc/cp/semantics.cc
> +++ b/gcc/cp/semantics.cc
> @@ -2453,6 +2453,8 @@ finish_parenthesized_expr (cp_expr expr)
>    tree stripped_expr = tree_strip_any_location_wrapper (expr);
>    if (TREE_CODE (stripped_expr) == STRING_CST)
>      PAREN_STRING_LITERAL_P (stripped_expr) = 1;
> +  else if (PACK_INDEX_P (stripped_expr))
> +    PACK_INDEX_PARENTHESIZED_P (stripped_expr) = true;
>  
>    expr = cp_expr (force_paren_expr (expr), expr.get_location ());
>  
> @@ -4848,24 +4850,38 @@ finish_type_pack_element (tree idx, tree types, tsubst_flags_t complain)
>    if (TREE_CODE (idx) != INTEGER_CST || !INTEGRAL_TYPE_P (TREE_TYPE (idx)))
>      {
>        if (complain & tf_error)
> -	error ("%<__type_pack_element%> index is not an integral constant");
> +	error ("pack index is not an integral constant");
>        return error_mark_node;
>      }
>    if (tree_int_cst_sgn (idx) < 0)
>      {
>        if (complain & tf_error)
> -	error ("%<__type_pack_element%> index is negative");
> +	error ("pack index is negative");
>        return error_mark_node;
>      }
>    if (wi::to_widest (idx) >= TREE_VEC_LENGTH (types))
>      {
>        if (complain & tf_error)
> -	error ("%<__type_pack_element%> index is out of range");
> +	error ("pack index is out of range");
>        return error_mark_node;
>      }
>    return TREE_VEC_ELT (types, tree_to_shwi (idx));
>  }
>  
> +/* In a pack-index T...[N], return the element at index IDX within TYPES.
> +   PARENTHESIZED_P is true iff the pack index was wrapped in ().  */
> +
> +tree
> +pack_index_element (tree idx, tree types, bool parenthesized_p,
> +		    tsubst_flags_t complain)
> +{
> +  tree r = finish_type_pack_element (idx, types, complain);
> +  if (parenthesized_p)
> +    /* For the benefit of decltype(auto).  */
> +    r = force_paren_expr (r);
> +  return r;
> +}
> +
>  /* Implement the __direct_bases keyword: Return the direct base classes
>     of type.  */
>  
> diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc
> index 911260685f1..53fcbc7273e 100644
> --- a/gcc/cp/tree.cc
> +++ b/gcc/cp/tree.cc
> @@ -4245,6 +4245,15 @@ cp_tree_equal (tree t1, tree t2)
>  	return false;
>        return true;
>  
> +    case PACK_INDEX_EXPR:
> +      if (!cp_tree_equal (PACK_INDEX_PACK (t1),
> +			  PACK_INDEX_PACK (t2)))
> +	return false;
> +      if (!cp_tree_equal (PACK_INDEX_INDEX (t1),
> +			  PACK_INDEX_INDEX (t2)))
> +	return false;
> +      return true;
> +
>      case COMPONENT_REF:
>        /* If we're comparing contract conditions of overrides, member references
>  	 compare equal if they designate the same member.  */
> @@ -5585,6 +5594,13 @@ cp_walk_subtrees (tree *tp, int *walk_subtrees_p, walk_tree_fn func,
>        *walk_subtrees_p = 0;
>        break;
>  
> +    case PACK_INDEX_TYPE:
> +    case PACK_INDEX_EXPR:
> +      WALK_SUBTREE (PACK_INDEX_PACK (t));
> +      WALK_SUBTREE (PACK_INDEX_INDEX (t));
> +      *walk_subtrees_p = 0;
> +      break;
> +
>      case CAST_EXPR:
>      case REINTERPRET_CAST_EXPR:
>      case STATIC_CAST_EXPR:
> diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc
> index 6ce1bb61fe7..06c632b9ee2 100644
> --- a/gcc/cp/typeck.cc
> +++ b/gcc/cp/typeck.cc
> @@ -1623,6 +1623,12 @@ structural_comptypes (tree t1, tree t2, int strict)
>  	      && comp_template_args (PACK_EXPANSION_EXTRA_ARGS (t1),
>  				     PACK_EXPANSION_EXTRA_ARGS (t2)));
>  
> +    case PACK_INDEX_TYPE:
> +      return (cp_tree_equal (PACK_INDEX_PACK (t1),
> +			     PACK_INDEX_PACK (t2))
> +	      && cp_tree_equal (PACK_INDEX_INDEX (t1),
> +				PACK_INDEX_INDEX (t2)));
> +
>      case DECLTYPE_TYPE:
>        if (DECLTYPE_TYPE_ID_EXPR_OR_MEMBER_ACCESS_P (t1)
>            != DECLTYPE_TYPE_ID_EXPR_OR_MEMBER_ACCESS_P (t2))
> diff --git a/gcc/testsuite/g++.dg/cpp26/pack-indexing1.C b/gcc/testsuite/g++.dg/cpp26/pack-indexing1.C
> new file mode 100644
> index 00000000000..e8c7a90ae58
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp26/pack-indexing1.C
> @@ -0,0 +1,102 @@
> +// P2662R3 - Pack Indexing
> +// PR c++/113798
> +// { dg-do compile { target c++17 } }
> +// { dg-options "" }
> +
> +template<class, class> struct same_type;
> +template<class T> struct same_type<T, T> {};
> +
> +template<int I, typename... Ts>
> +using Type = Ts...[I]; // { dg-warning "pack indexing only available with" "" { target c++23_down } }
> +
> +template<int I, auto... Ts>
> +constexpr auto Var = Ts...[I]; // { dg-warning "pack indexing only available with" "" { target c++23_down } }
> +
> +template <int I, auto...Ts>
> +int
> +foo ()
> +{
> +  return Ts...[I]; // { dg-warning "pack indexing only available with" "" { target c++23_down } }
> +}
> +
> +template<typename... Ts>
> +struct S {
> +  Ts...[0] a; // { dg-warning "pack indexing only available with" "" { target c++23_down } }
> +#if __cpp_concepts >= 201907L
> +  void foo (auto... Vs) {
> +    decltype(Vs...[1]) d1 = Vs...[1]; // { dg-warning "pack indexing only available with" "" { target { c++20 && c++23_down } } }
> +  }
> +#endif
> +};
> +
> +int
> +g ()
> +{
> +  using U = Type<1, char, int, float>;
> +  using U = int;
> +
> +  constexpr auto V = Var<2, 0, 1, 42>;
> +  static_assert (V == 42);
> +
> +  U r = foo<2, 0, 1, 42>();
> +
> +  return r;
> +}
> +
> +void
> +fn1 ()
> +{
> +  int i = 0;
> +  [&i](auto... pack) {
> +    // type is int
> +    decltype(pack...[0]) x5 = 42; // { dg-warning "pack indexing only available with" "" { target c++23_down } }
> +    // type is int&
> +    [[maybe_unused]] decltype((pack...[0])) x6 = i; // { dg-warning "pack indexing only available with" "" { target c++23_down } }
> +  }(0);
> +}
> +
> +#if __cpp_concepts >= 201907L
> +int
> +bar (auto... pack)
> +{
> +  (void) pack...[0]; // { dg-warning "pack indexing only available with" "" { target { c++20 && c++23_down } } }
> +  int x = pack...[0]; // { dg-warning "pack indexing only available with" "" { target { c++20 && c++23_down } } }
> +  return x;
> +}
> +#endif
> +
> +template<auto...pack>
> +void
> +fn2 ()
> +{
> +  [[maybe_unused]] decltype(pack...[0]) x1; // { dg-warning "pack indexing only available with" "" { target c++23_down } }
> +  [[maybe_unused]] decltype((pack...[0])) x2; // { dg-warning "pack indexing only available with" "" { target c++23_down } }
> +  same_type<decltype(x1), int>();
> +  same_type<decltype(x2), int>();
> +}
> +
> +template<typename... T>
> +void
> +fn3 (int p)
> +{
> +  T...[0] a = p; // { dg-warning "pack indexing only available with" "" { target c++23_down } }
> +  (T...[0])(a);  // { dg-warning "pack indexing only available with" "" { target c++23_down } }
> +}
> +
> +template<int... Is>
> +void fn4 ()
> +{
> +  same_type<decltype(Is...[0]), int>();	// { dg-warning "pack indexing only available with" "" { target c++23_down } }
> +  same_type<decltype((Is...[0])), int>(); // { dg-warning "pack indexing only available with" "" { target c++23_down } }
> +}
> +
> +void
> +g3 ()
> +{
> +  fn2<0>();
> +#if __cpp_concepts >= 201907L
> +  bar (0);
> +#endif
> +  S<int> s;
> +  fn4<0, 1, 2>();
> +}
> diff --git a/gcc/testsuite/g++.dg/cpp26/pack-indexing2.C b/gcc/testsuite/g++.dg/cpp26/pack-indexing2.C
> new file mode 100644
> index 00000000000..ec32527ed80
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp26/pack-indexing2.C
> @@ -0,0 +1,111 @@
> +// P2662R3 - Pack Indexing
> +// PR c++/113798
> +// { dg-do compile { target c++26 } }
> +// Test invalid cases.
> +
> +template<int I, typename... Ts>
> +using Type = Typo...[I]; // { dg-error "does not name a type" }
> +
> +template<int I, auto... Ts>
> +constexpr auto Var = Typo...[I]; // { dg-error "no parameter packs" }
> +
> +template<typename... Ts>
> +void foo(Ts...[]);  // { dg-error "pack index missing" }
> +
> +template <typename... Ts>
> +void f(Ts...[1]);
> +
> +template<int... N>
> +int f2 (X... [N]); // { dg-error "contains no parameter packs" }
> +
> +struct X;
> +
> +template<typename T>
> +struct TX;
> +
> +template <typename T, auto V, template<typename> typename Tp>
> +void
> +bad (int i)
> +{
> +  i...[0];  // { dg-error "no parameter packs" }
> +  V...[0];  // { dg-error "no parameter packs" }
> +  X...[0] x;  // { dg-error "no parameter packs" }
> +  T...[0] t;  // { dg-error "no parameter packs" }
> +  Tp...[0] tp;  // { dg-error "expected" }
> +
> +  X...[0] xarr[1];  // { dg-error "no parameter packs" }
> +  T...[0] tarr[1];  // { dg-error "no parameter packs" }
> +  Tp...[0] tparr[1];  // { dg-error "expected" }
> +}
> +
> +template<int N>
> +int
> +getT (auto... Ts)
> +{
> +  return Ts...[N]; // { dg-error "pack index is out of range" }
> +}
> +
> +template<int N>
> +int
> +getT2 (auto... Ts)
> +{
> +  return Ts...[N]; // { dg-error "pack index is negative" }
> +}
> +
> +template<auto N, typename... Ts>
> +void
> +badtype ()
> +{
> +  Ts...[N] t; // { dg-error "pack index is out of range" }
> +}
> +
> +template<auto N, typename... Ts>
> +void
> +badtype2 ()
> +{
> +  Ts...[N] t; // { dg-error "pack index is negative" }
> +}
> +
> +int nonconst () { return 42; }
> +
> +template<typename... Ts>
> +void
> +badindex ()
> +{
> +  Ts...[nonconst ()] t; // { dg-error "pack index is not an integral constant" }
> +  // { dg-error "non-.constexpr. function" "" { target *-*-* } .-1 }
> +}
> +
> +template<typename... Ts>
> +struct broken {
> +  Ts...1;  // { dg-error "expected" }
> +  Ts...[;  // { dg-error "invalid" }
> +  Ts...[1;  // { dg-error "invalid" }
> +  Ts...[];  // { dg-error "pack index missing" }
> +
> +  void foo (auto...Vs) {
> +    decltype(Vs...[1]) d1 = Vs...[]; // { dg-error "pack index missing" }
> +    decltype(Vs...[1]) d2 = Vs...[; // { dg-error "expected" }
> +  }
> +};
> +
> +int main()
> +{
> +   // void f<int, double>(int [1], double [1])
> +  f<int, double>(nullptr, nullptr); // { dg-error "no matching function" }
> +  bad<int, 0, TX>(42);
> +
> +  getT<0>(); // { dg-message "required from here" }
> +  getT<1>();  // { dg-message "required from here" }
> +  getT2<-1>();  // { dg-message "required from here" }
> +
> +  badtype<0>(); // { dg-message "required from here" }
> +  badtype<1, int>(); // { dg-message "required from here" }
> +  badtype2<-1>(); // { dg-message "required from here" }
> +  badtype2<-1, int>(); // { dg-message "required from here" }
> +
> +  badindex<int, int, int>();
> +
> +  bool b = nothere...[0]; // { dg-error "no parameter packs" }
> +  using E = nothere...[0]; // { dg-error "does not name a type" }
> +}
> diff --git a/gcc/testsuite/g++.dg/cpp26/pack-indexing3.C b/gcc/testsuite/g++.dg/cpp26/pack-indexing3.C
> new file mode 100644
> index 00000000000..8c10b307f3a
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp26/pack-indexing3.C
> @@ -0,0 +1,41 @@
> +// P2662R3 - Pack Indexing
> +// PR c++/113798
> +// { dg-do compile { target c++26 } }
> +// From LLVM's cxx2c-pack-indexing.cpp.
> +
> +template<typename...>
> +struct X { };
> +
> +template<typename... T>
> +requires requires(T...[0]) { {T...[0](0)}; }
> +struct S : T...[1] {
> +  [[maybe_unused]] T...[1] base = {};
> +  using foo = T...[1];
> +  S() : T...[1]() { }
> +  X<T...[0]> x;
> +  const T...[0] f(T...[0]&& parm) noexcept((T...[0])0) {
> +    T...[0] (*test)(const volatile T...[0]**);
> +    thread_local T...[0] d;
> +    [[maybe_unused]] T...[0] a = parm;
> +    auto ptr = new T...[0](0);
> +    (*ptr).~T...[0]();
> +    return T...[0](0);
> +    typename T...[1]::foo b = 0;
> +    T...[1]::i = 0;
> +    return (T...[0])(a);
> +    new T...[0];
> +    [[maybe_unused]] auto l = []<T...[0]>(T...[0][1]) -> T...[0]{ return {}; };
> +    [[maybe_unused]] auto _ = l.template operator()<T...[0]{}>({0});
> +  }
> +  operator T...[0]() const { }
> +};
> +
> +struct base {
> +    using foo = int;
> +    static inline int i = 42;
> +};
> +
> +int main()
> +{
> +  S<int, base>().f(0);
> +}
> diff --git a/gcc/testsuite/g++.dg/cpp26/pack-indexing4.C b/gcc/testsuite/g++.dg/cpp26/pack-indexing4.C
> new file mode 100644
> index 00000000000..8decf3064bc
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp26/pack-indexing4.C
> @@ -0,0 +1,65 @@
> +// P2662R3 - Pack Indexing
> +// PR c++/113798
> +// { dg-do compile { target c++26 } }
> +// From LLVM's cxx2c-pack-indexing.cpp.
> +
> +template <class, class>
> +constexpr bool is_same = false;
> +template <class T>
> +constexpr bool is_same<T, T> = true;
> +
> +template <typename T>
> +constexpr bool
> +f (auto&&... p)
> +{
> +  return is_same<T, decltype(p...[0])>;
> +}
> +
> +void
> +g ()
> +{
> +    int a = 0;
> +    const int b = 0;
> +    static_assert(f<int&&>(0));
> +    static_assert(f<int&>(a));
> +    static_assert(f<const int&>(b));
> +}
> +
> +template<auto... p>
> +struct A {
> +  enum E {
> +    x = p...[0]
> +  };
> +};
> +static_assert(A<42>::x == 42);
> +
> +struct S { };
> +template<auto... p>
> +constexpr auto constant_initializer = p...[0];
> +constexpr auto InitOk = constant_initializer<S{}>;
> +
> +consteval int evaluate(auto... p) {
> +    return p...[0];
> +}
> +constexpr int x = evaluate(42, S{});
> +static_assert(x == 42);
> +
> +template <auto... Is>
> +struct IL{};
> +
> +template <typename... Ts>
> +struct TL{};
> +
> +template <typename Tl, typename Il>
> +struct SpliceImpl;
> +
> +template <typename... Ts, auto... Is>
> +struct SpliceImpl<TL<Ts...>, IL<Is...>>
> +{
> +    using type = TL<Ts...[Is]...>;
> +};
> +
> +template <typename Tl, typename Il>
> +using Splice = typename SpliceImpl<Tl, Il>::type;
> +using type = Splice<TL<char, short, long, double>, IL<1, 2>>;
> +static_assert(is_same<type, TL<short, long>>);
> diff --git a/gcc/testsuite/g++.dg/cpp26/pack-indexing5.C b/gcc/testsuite/g++.dg/cpp26/pack-indexing5.C
> new file mode 100644
> index 00000000000..901956e2dae
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp26/pack-indexing5.C
> @@ -0,0 +1,41 @@
> +// P2662R3 - Pack Indexing
> +// PR c++/113798
> +// { dg-do compile { target c++26 } }
> +
> +template<class, class> struct same_type;
> +template<class T> struct same_type<T, T> {};
> +
> +void
> +fn1 (auto... Ts)
> +{
> +  same_type<decltype(Ts...[0]), int>();
> +  same_type<decltype((Ts...[0])), int&>();
> +  same_type<decltype(Ts...[1]), unsigned int>();
> +  same_type<decltype((Ts...[1])), unsigned int&>();
> +}
> +
> +template<auto... Is>
> +void
> +fn2 ()
> +{
> +  same_type<decltype(Is...[0]), int>();
> +  same_type<decltype((Is...[0])), int>();
> +  same_type<decltype(Is...[1]), unsigned int>();
> +  same_type<decltype((Is...[1])), unsigned int>();
> +  same_type<decltype(Is...[2]), double>();
> +  same_type<decltype((Is...[2])), double>();
> +  same_type<decltype(Is...[3]), float>();
> +  same_type<decltype((Is...[3])), float>();
> +  same_type<decltype(Is...[4]), unsigned char>();
> +  same_type<decltype((Is...[4])), unsigned char>();
> +}
> +
> +static constexpr unsigned char c = 'A';
> +
> +void
> +g ()
> +{
> +  int i = 42;
> +  fn1 (i, 42u);
> +  fn2<0, 1u, 2.0, 3.f, c>();
> +}
> diff --git a/gcc/testsuite/g++.dg/cpp26/pack-indexing6.C b/gcc/testsuite/g++.dg/cpp26/pack-indexing6.C
> new file mode 100644
> index 00000000000..9af88765254
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp26/pack-indexing6.C
> @@ -0,0 +1,51 @@
> +// P2662R3 - Pack Indexing
> +// PR c++/113798
> +// { dg-do compile { target c++26 } }
> +
> +template<class, class> struct same_type;
> +template<class T> struct same_type<T, T> {};
> +
> +void
> +fn1 (auto... Ts)
> +{
> +  decltype(auto) a1 = Ts...[0];
> +  same_type<decltype(a1), int>();
> +  decltype(auto) a2 = (Ts...[0]);
> +  same_type<decltype(a2), int&>();
> +}
> +
> +template<auto... Is>
> +void
> +fn2 ()
> +{
> +  decltype(auto) a1 = Is...[0];
> +  same_type<decltype(a1), int>();
> +  decltype(auto) a2 = (Is...[0]);
> +  same_type<decltype(a2), int>();
> +  decltype(auto) a3 = Is...[1];
> +  same_type<decltype(a3), unsigned int>();
> +  decltype(auto) a4 = (Is...[1]);
> +  same_type<decltype(a4), unsigned int>();
> +  decltype(auto) a5 = Is...[2];
> +  same_type<decltype(a5), double>();
> +  decltype(auto) a6 = (Is...[2]);
> +  same_type<decltype(a6), double>();
> +  decltype(auto) a7 = Is...[3];
> +  same_type<decltype(a7), float>();
> +  decltype(auto) a8 = (Is...[3]);
> +  same_type<decltype(a8), float>();
> +  decltype(auto) a9 = Is...[4];
> +  same_type<decltype(a9), unsigned char>();
> +  decltype(auto) a10 = (Is...[4]);
> +  same_type<decltype(a10), unsigned char>();
> +}
> +
> +static constexpr unsigned char c = 'A';
> +
> +void
> +g ()
> +{
> +  int i = 42;
> +  fn1 (i, 42u);
> +  fn2<0, 1u, 2.0, 3.f, c>();
> +}
> diff --git a/gcc/testsuite/g++.dg/cpp26/pack-indexing7.C b/gcc/testsuite/g++.dg/cpp26/pack-indexing7.C
> new file mode 100644
> index 00000000000..06e2a3d0dc7
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp26/pack-indexing7.C
> @@ -0,0 +1,16 @@
> +// P2662R3 - Pack Indexing
> +// PR c++/113798
> +// { dg-do compile { target c++26 } }
> +
> +template <int I, auto...Ts>
> +decltype(Ts...[I])
> +foo ()			      // { dg-message "sorry, unimplemented: mangling" }
> +{
> +  return Ts...[I];
> +}
> +
> +int
> +g ()
> +{
> +  return foo<2, 0, 1, 42>();
> +}
> diff --git a/gcc/testsuite/g++.dg/cpp26/pack-indexing8.C b/gcc/testsuite/g++.dg/cpp26/pack-indexing8.C
> new file mode 100644
> index 00000000000..2b1b67c0841
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp26/pack-indexing8.C
> @@ -0,0 +1,23 @@
> +// P2662R3 - Pack Indexing
> +// PR c++/113798
> +// { dg-do run { target c++26 } }
> +
> +#include <initializer_list>
> +
> +template<typename... Ts>
> +int
> +g (auto... Is)
> +{
> +  std::initializer_list<Ts...[0]> l{ Is...[0], Is...[1], Is...[2], Is...[3], Is...[4] };
> +  int sum = 0;
> +  for (auto x : l)
> +    sum += x;
> +  return sum;
> +}
> +
> +int
> +main ()
> +{
> +  if (g<int> (1, 2, 3, 4, 5) != 15)
> +    __builtin_abort ();
> +}
> diff --git a/gcc/testsuite/g++.dg/cpp26/pack-indexing9.C b/gcc/testsuite/g++.dg/cpp26/pack-indexing9.C
> new file mode 100644
> index 00000000000..468ae9dc4d1
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp26/pack-indexing9.C
> @@ -0,0 +1,27 @@
> +// P2662R3 - Pack Indexing
> +// PR c++/113798
> +// { dg-do compile { target c++26 } }
> +// From <https://github.com/itanium-cxx-abi/cxx-abi/issues/175>.
> +
> +template <class... T> struct tuple {
> +  template <unsigned I> T...[I] get();  // { dg-message "sorry, unimplemented: mangling" }
> +};
> +
> +int
> +g ()
> +{
> +  tuple<int> t;
> +  return t.get<0>();
> +}
> +
> +template<typename T, typename U> concept C = true;
> +template<typename ...T> struct A {
> +    template<int I, typename ...U> void f(T...[I], U...[I]) requires C<T...[I], U...[I]>;  // { dg-message "sorry, unimplemented: mangling" }
> +};
> +
> +void
> +h ()
> +{
> +  A<char, int, double> a;
> +  a.f<1, int, int, char>(1, 2);
> +}
> diff --git a/gcc/testsuite/g++.dg/modules/pack-index-1_a.C b/gcc/testsuite/g++.dg/modules/pack-index-1_a.C
> new file mode 100644
> index 00000000000..30ae189711c
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/modules/pack-index-1_a.C
> @@ -0,0 +1,18 @@
> +// { dg-module-do run }
> +// { dg-additional-options { -std=c++26 -fmodules-ts } }
> +
> +export module packing1;
> +// { dg-module-cmi "packing1" }
> +
> +export template<int I, typename... Ts>
> +using Type = Ts...[I];
> +
> +export template<int I, auto... Ts>
> +constexpr auto Var = Ts...[I];
> +
> +export template <int I, auto...Ts>
> +int
> +foo ()
> +{
> +  return Ts...[I];
> +}
> diff --git a/gcc/testsuite/g++.dg/modules/pack-index-1_b.C b/gcc/testsuite/g++.dg/modules/pack-index-1_b.C
> new file mode 100644
> index 00000000000..dce6f5774d3
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/modules/pack-index-1_b.C
> @@ -0,0 +1,15 @@
> +// { dg-additional-options { -std=c++26 -fmodules-ts } }
> +
> +import packing1;
> +
> +int
> +main ()
> +{
> +  using U = Type<1, char, int, float>;
> +  using U = int;
> +
> +  U r = foo<2, 0, 1, 42>();
> +
> +  constexpr auto V = Var<2, 0, 1, 42>;
> +  static_assert (V == 42);
> +}
> diff --git a/libstdc++-v3/testsuite/20_util/tuple/element_access/get_neg.cc b/libstdc++-v3/testsuite/20_util/tuple/element_access/get_neg.cc
> index 2346c29b80f..9e002f4d47a 100644
> --- a/libstdc++-v3/testsuite/20_util/tuple/element_access/get_neg.cc
> +++ b/libstdc++-v3/testsuite/20_util/tuple/element_access/get_neg.cc
> @@ -61,4 +61,4 @@ test03()
>  
>  // { dg-error "tuple index must be in range" "" { target *-*-* } 0 }
>  // { dg-prune-output "no type named 'type' in .*_Nth_type" }
> -// { dg-prune-output "'__type_pack_element' index is out of range" }
> +// { dg-prune-output "pack index is out of range" }
> 
> base-commit: f003834badbfd9d0c0ad132de8b2f3d550ed120f
> -- 
> 2.47.0
> 
>
  

Patch

diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc
index c141c0a2844..3c1a10bc13f 100644
--- a/gcc/cp/constexpr.cc
+++ b/gcc/cp/constexpr.cc
@@ -9939,6 +9939,11 @@  potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
     case EXPR_PACK_EXPANSION:
       return RECUR (PACK_EXPANSION_PATTERN (t), want_rval);
 
+    case PACK_INDEX_EXPR:
+      if (!RECUR (PACK_INDEX_PACK (t), want_rval))
+	return false;
+      return RECUR (PACK_INDEX_INDEX (t), want_rval);
+
     case INDIRECT_REF:
       {
         tree x = TREE_OPERAND (t, 0);
diff --git a/gcc/cp/cp-objcp-common.cc b/gcc/cp/cp-objcp-common.cc
index 7a0636f1653..fe663ecaeb6 100644
--- a/gcc/cp/cp-objcp-common.cc
+++ b/gcc/cp/cp-objcp-common.cc
@@ -642,6 +642,7 @@  cp_common_init_ts (void)
   MARK_TS_TYPE_NON_COMMON (TEMPLATE_TEMPLATE_PARM);
   MARK_TS_TYPE_NON_COMMON (TEMPLATE_TYPE_PARM);
   MARK_TS_TYPE_NON_COMMON (TYPE_PACK_EXPANSION);
+  MARK_TS_TYPE_NON_COMMON (PACK_INDEX_TYPE);
 
   /* Statements.  */
   MARK_TS_EXP (CLEANUP_STMT);
@@ -700,6 +701,7 @@  cp_common_init_ts (void)
   MARK_TS_EXP (NONTYPE_ARGUMENT_PACK);
   MARK_TS_EXP (UNARY_LEFT_FOLD_EXPR);
   MARK_TS_EXP (UNARY_RIGHT_FOLD_EXPR);
+  MARK_TS_EXP (PACK_INDEX_EXPR);
 
   /* Constraints.  */
   MARK_TS_EXP (COMPOUND_REQ);
diff --git a/gcc/cp/cp-tree.def b/gcc/cp/cp-tree.def
index 3de6923f771..fa445ec2781 100644
--- a/gcc/cp/cp-tree.def
+++ b/gcc/cp/cp-tree.def
@@ -397,6 +397,14 @@  DEFTREECODE (TYPE_PACK_EXPANSION, "type_pack_expansion", tcc_type, 0)
    but will be used for expressions.  */
 DEFTREECODE (EXPR_PACK_EXPANSION, "expr_pack_expansion", tcc_expression, 3)
 
+/* Represents a pack index Ts...[I], yielding a type.  PACK_INDEX_PACK is
+   the pack expansion Ts, PACK_INDEX_INDEX the index I.  */
+DEFTREECODE (PACK_INDEX_TYPE, "pack_index_type", tcc_type, 0)
+
+/* Represents a pack index Ts...[I], yielding an expression.  PACK_INDEX_PACK
+   is the pack expansion Ts, PACK_INDEX_INDEX the index I.  */
+DEFTREECODE (PACK_INDEX_EXPR, "pack_index_expr", tcc_expression, 2)
+
 /* Selects the Ith parameter out of an argument pack. This node will
    be used when instantiating pack expansions; see
    tsubst_pack_expansion.
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index f98a1de42ca..17eccddc1ae 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -451,6 +451,7 @@  extern GTY(()) tree cp_global_trees[CPTI_MAX];
       ATOMIC_CONSTR_MAP_INSTANTIATED_P (in ATOMIC_CONSTR)
       contract_semantic (in ASSERTION_, PRECONDITION_, POSTCONDITION_STMT)
       RETURN_EXPR_LOCAL_ADDR_P (in RETURN_EXPR)
+      PACK_INDEX_PARENTHESIZED_P (in PACK_INDEX_*)
    1: IDENTIFIER_KIND_BIT_1 (in IDENTIFIER_NODE)
       TI_PENDING_TEMPLATE_FLAG.
       TEMPLATE_PARMS_FOR_INLINE.
@@ -2258,7 +2259,8 @@  enum languages { lang_c, lang_cplusplus };
    || TREE_CODE (T) == BOUND_TEMPLATE_TEMPLATE_PARM	\
    || TREE_CODE (T) == DECLTYPE_TYPE			\
    || TREE_CODE (T) == TRAIT_TYPE			\
-   || TREE_CODE (T) == DEPENDENT_OPERATOR_TYPE)
+   || TREE_CODE (T) == DEPENDENT_OPERATOR_TYPE		\
+   || PACK_INDEX_P (T))
 
 /* Nonzero if T is a class (or struct or union) type.  Also nonzero
    for template type parameters, typename types, and instantiated
@@ -4001,6 +4003,9 @@  struct GTY(()) lang_decl {
 #define PACK_EXPANSION_CHECK(NODE) \
   TREE_CHECK2 (NODE, TYPE_PACK_EXPANSION, EXPR_PACK_EXPANSION)
 
+#define PACK_INDEX_CHECK(NODE) \
+  TREE_CHECK2 (NODE, PACK_INDEX_TYPE, PACK_INDEX_EXPR)
+
 /* Extracts the type or expression pattern from a TYPE_PACK_EXPANSION or
    EXPR_PACK_EXPANSION.  */
 #define PACK_EXPANSION_PATTERN(NODE)                            \
@@ -4025,6 +4030,22 @@  struct GTY(()) lang_decl {
     ? &TYPE_MAX_VALUE_RAW (NODE)			\
     : &TREE_OPERAND ((NODE), 2))
 
+/* True if NODE is a pack index.  */
+#define PACK_INDEX_P(NODE)                 \
+  (TREE_CODE (NODE) == PACK_INDEX_TYPE     \
+   || TREE_CODE (NODE) == PACK_INDEX_EXPR)
+
+/* For a pack index T...[N], the pack expansion T.  */
+#define PACK_INDEX_PACK(NODE) \
+  (TREE_CODE (PACK_INDEX_CHECK (NODE)) == PACK_INDEX_TYPE \
+   ? TREE_TYPE (NODE) : TREE_OPERAND (NODE, 0))
+
+/* For a pack index T...[N], the index N.  */
+#define PACK_INDEX_INDEX(NODE) \
+  *(TREE_CODE (PACK_INDEX_CHECK (NODE)) == PACK_INDEX_TYPE \
+    ? &TYPE_MAX_VALUE_RAW (NODE)			\
+    : &TREE_OPERAND ((NODE), 1))
+
 /* True iff this pack expansion is within a function context.  */
 #define PACK_EXPANSION_LOCAL_P(NODE) \
   TREE_LANG_FLAG_0 (PACK_EXPANSION_CHECK (NODE))
@@ -4042,6 +4063,11 @@  struct GTY(()) lang_decl {
 #define PACK_EXPANSION_FORCE_EXTRA_ARGS_P(NODE) \
   TREE_LANG_FLAG_3 (PACK_EXPANSION_CHECK (NODE))
 
+/* Indicates whether a pack expansion has been parenthesized.  Used for
+   a pack expansion in a decltype.  */
+#define PACK_INDEX_PARENTHESIZED_P(NODE) \
+  TREE_LANG_FLAG_1 (PACK_INDEX_CHECK (NODE))
+
 /* True iff the wildcard can match a template parameter pack.  */
 #define WILDCARD_PACK_P(NODE) TREE_LANG_FLAG_0 (NODE)
 
@@ -7581,6 +7607,7 @@  extern bool template_parameter_pack_p           (const_tree);
 extern bool function_parameter_pack_p		(const_tree);
 extern bool function_parameter_expanded_from_pack_p (tree, tree);
 extern tree make_pack_expansion                 (tree, tsubst_flags_t = tf_warning_or_error);
+extern tree make_pack_index			(tree, tree);
 extern bool check_for_bare_parameter_packs      (tree, location_t = UNKNOWN_LOCATION);
 extern tree build_template_info			(tree, tree);
 extern tree get_template_info			(const_tree);
@@ -7906,6 +7933,8 @@  extern tree finish_underlying_type	        (tree);
 extern tree calculate_bases                     (tree, tsubst_flags_t);
 extern tree finish_bases                        (tree, bool);
 extern tree calculate_direct_bases              (tree, tsubst_flags_t);
+extern tree pack_index_element			(tree, tree, bool,
+						 tsubst_flags_t);
 extern tree finish_offsetof			(tree, tree, location_t);
 extern void finish_decl_cleanup			(tree, tree);
 extern void finish_eh_cleanup			(tree);
diff --git a/gcc/cp/cxx-pretty-print.cc b/gcc/cp/cxx-pretty-print.cc
index f4203123737..15d41b55557 100644
--- a/gcc/cp/cxx-pretty-print.cc
+++ b/gcc/cp/cxx-pretty-print.cc
@@ -1220,6 +1220,13 @@  cxx_pretty_printer::expression (tree t)
       pp_cxx_ws_string (this, "...");
       break;
 
+    case PACK_INDEX_EXPR:
+      expression (PACK_INDEX_PACK (t));
+      pp_cxx_left_bracket (this);
+      expression (PACK_INDEX_INDEX (t));
+      pp_cxx_right_bracket (this);
+      break;
+
     case UNARY_LEFT_FOLD_EXPR:
       pp_cxx_unary_left_fold_expression (this, t);
       break;
@@ -1920,6 +1927,13 @@  cxx_pretty_printer::type_id (tree t)
       pp_cxx_ws_string (this, "...");
       break;
 
+    case PACK_INDEX_TYPE:
+      type_id (PACK_INDEX_PACK (t));
+      pp_cxx_left_bracket (this);
+      expression (PACK_INDEX_INDEX (t));
+      pp_cxx_right_bracket (this);
+      break;
+
     case TYPE_ARGUMENT_PACK:
       {
 	tree args = ARGUMENT_PACK_ARGS (t);
diff --git a/gcc/cp/error.cc b/gcc/cp/error.cc
index 8381f950488..229705d0fa2 100644
--- a/gcc/cp/error.cc
+++ b/gcc/cp/error.cc
@@ -820,6 +820,13 @@  dump_type (cxx_pretty_printer *pp, tree t, int flags)
       pp_cxx_ws_string (pp, "...");
       break;
 
+    case PACK_INDEX_TYPE:
+      dump_type (pp, PACK_INDEX_PACK (t), flags);
+      pp_cxx_left_bracket (pp);
+      dump_expr (pp, PACK_INDEX_INDEX (t), flags & ~TFF_EXPR_IN_PARENS);
+      pp_cxx_right_bracket (pp);
+      break;
+
     case TYPE_ARGUMENT_PACK:
       dump_template_argument (pp, t, flags);
       break;
@@ -1094,6 +1101,7 @@  dump_type_prefix (cxx_pretty_printer *pp, tree t, int flags)
     case TYPE_PACK_EXPANSION:
     case FIXED_POINT_TYPE:
     case NULLPTR_TYPE:
+    case PACK_INDEX_TYPE:
       dump_type (pp, t, flags);
       pp->set_padding (pp_before);
       break;
@@ -1226,6 +1234,7 @@  dump_type_suffix (cxx_pretty_printer *pp, tree t, int flags)
     case TYPE_PACK_EXPANSION:
     case FIXED_POINT_TYPE:
     case NULLPTR_TYPE:
+    case PACK_INDEX_TYPE:
       break;
 
     default:
@@ -3151,6 +3160,13 @@  dump_expr (cxx_pretty_printer *pp, tree t, int flags)
       pp->expression (t);
       break;
 
+    case PACK_INDEX_EXPR:
+      pp->expression (PACK_INDEX_PACK (t));
+      pp_cxx_left_bracket (pp);
+      pp->expression (PACK_INDEX_INDEX (t));
+      pp_cxx_right_bracket (pp);
+      break;
+
     case TRUTH_AND_EXPR:
     case TRUTH_OR_EXPR:
     case TRUTH_XOR_EXPR:
diff --git a/gcc/cp/mangle.cc b/gcc/cp/mangle.cc
index 4d46c0917a9..c103b50b1f1 100644
--- a/gcc/cp/mangle.cc
+++ b/gcc/cp/mangle.cc
@@ -2669,6 +2669,12 @@  write_type (tree type)
 		     "use library traits instead", type);
 	      break;
 
+	    case PACK_INDEX_TYPE:
+	      /* TODO Mangle pack indexing
+		 <https://github.com/itanium-cxx-abi/cxx-abi/issues/175>.  */
+	      sorry ("mangling type pack index");
+	      break;
+
 	    case LANG_TYPE:
 	      /* fall through.  */
 
diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc
index f72e0154dd6..3d69c735a17 100644
--- a/gcc/cp/module.cc
+++ b/gcc/cp/module.cc
@@ -9209,6 +9209,11 @@  trees_out::type_node (tree type)
       tree_node (PACK_EXPANSION_EXTRA_ARGS (type));
       break;
 
+    case PACK_INDEX_TYPE:
+      tree_node (PACK_INDEX_PACK (type));
+      tree_node (PACK_INDEX_INDEX (type));
+      break;
+
     case TYPENAME_TYPE:
       {
 	tree_node (TYPE_CONTEXT (type));
@@ -9776,6 +9781,15 @@  trees_in::tree_node (bool is_use)
 	    }
 	    break;
 
+	  case PACK_INDEX_TYPE:
+	    {
+	      tree pack = tree_node ();
+	      tree index = tree_node ();
+	      if (!get_overrun ())
+		res = make_pack_index (pack, index);
+	    }
+	    break;
+
 	  case TYPENAME_TYPE:
 	    {
 	      tree ctx = tree_node ();
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index c1375ecdbb5..e562ba723ad 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -5739,6 +5739,54 @@  cp_parser_fold_expression (cp_parser *parser, tree expr1)
   return finish_binary_fold_expr (loc, expr1, expr2, op);
 }
 
+/*  Parse a pack-index-specifier:
+
+    pack-index-specifier:
+      typedef-name ... [ constant-expression ]
+
+    or a pack-index-expression:
+
+    pack-index-expression:
+      id-expression ... [ constant-expression ]
+
+    PACK is the parsed typedef-name or the id-expression.  Returns
+    either a PACK_INDEX_TYPE or PACK_INDEX_EXPR.  */
+
+static tree
+cp_parser_pack_index (cp_parser *parser, tree pack)
+{
+  if (cxx_dialect < cxx26)
+    pedwarn (cp_lexer_peek_token (parser->lexer)->location,
+	     OPT_Wc__26_extensions, "pack indexing only available with "
+	     "%<-std=c++2c%> or %<-std=gnu++2c%>");
+  /* Consume the '...' token.  */
+  cp_lexer_consume_token (parser->lexer);
+  /* Consume the '['.  */
+  cp_lexer_consume_token (parser->lexer);
+
+  if (cp_lexer_next_token_is (parser->lexer, CPP_CLOSE_SQUARE))
+    {
+      error_at (cp_lexer_peek_token (parser->lexer)->location,
+		"pack index missing");
+      cp_lexer_consume_token (parser->lexer);
+      return error_mark_node;
+    }
+
+  tree index = cp_parser_constant_expression (parser,
+					      /*allow_non_constant_p=*/false,
+					      /*non_constant_p=*/nullptr,
+					      /*strict_p=*/true);
+  /* Consume the ']'.  */
+  cp_parser_require (parser, CPP_CLOSE_SQUARE, RT_CLOSE_SQUARE);
+
+  if (TREE_CODE (pack) == TYPE_DECL)
+    pack = TREE_TYPE (pack);
+  pack = make_pack_expansion (pack);
+  if (pack == error_mark_node)
+    return error_mark_node;
+  return make_pack_index (pack, index);
+}
+
 /* Parse a primary-expression.
 
    primary-expression:
@@ -6368,6 +6416,12 @@  cp_parser_primary_expression (cp_parser *parser,
 	  = make_location (caret_loc, start_loc, finish_loc);
 
 	decl.set_location (combined_loc);
+
+	/* "T...[constant-expression]" is a C++26 pack-index-expression.  */
+	if (cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS)
+	    && cp_lexer_nth_token_is (parser->lexer, 2, CPP_OPEN_SQUARE))
+	  decl = cp_parser_pack_index (parser, decl);
+
 	return decl;
       }
 
@@ -6411,6 +6465,7 @@  missing_template_diag (location_t loc, diagnostic_t diag_kind = DK_WARNING)
    id-expression:
      unqualified-id
      qualified-id
+     pack-index-expression
 
    qualified-id:
      :: [opt] nested-name-specifier template [opt] unqualified-id
@@ -6593,7 +6648,9 @@  cp_parser_id_expression (cp_parser *parser,
      identifier
      operator-function-id
      conversion-function-id
-     ~ class-name
+     literal-operator-id
+     ~ type-name
+     ~ computed-type-specifier
      template-id
 
    If TEMPLATE_KEYWORD_P is TRUE, we have just seen the `template'
@@ -6900,6 +6957,14 @@  cp_parser_unqualified_id (cp_parser* parser,
 		    "typedef-name %qD used as destructor declarator",
 		    type_decl);
 
+	/* "~T...[N]" is a C++26 pack-index-specifier.  */
+	if (cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS)
+	    && cp_lexer_nth_token_is (parser->lexer, 2, CPP_OPEN_SQUARE))
+	  {
+	    type_decl = cp_parser_pack_index (parser, type_decl);
+	    return build_min_nt_loc (loc, BIT_NOT_EXPR, type_decl);
+	  }
+
 	return build_min_nt_loc (loc, BIT_NOT_EXPR, TREE_TYPE (type_decl));
       }
 
@@ -6970,9 +7035,10 @@  check_template_keyword_in_nested_name_spec (tree name)
      class-or-namespace-name :: nested-name-specifier [opt]
      class-or-namespace-name :: template nested-name-specifier [opt]
 
-   nested-name-specifier: [C++0x]
+   nested-name-specifier: [C++11]
      type-name ::
      namespace-name ::
+     computed-type-specifier ::
      nested-name-specifier identifier ::
      nested-name-specifier template [opt] simple-template-id ::
 
@@ -7080,6 +7146,10 @@  cp_parser_nested_name_specifier_opt (cp_parser *parser,
 	    }
 
 	  if (token->type != CPP_SCOPE
+	      /* See if a pack-index-specifier follows.  */
+	      && !(token->type == CPP_ELLIPSIS
+		   && cp_lexer_peek_nth_token (parser->lexer, 3)->type
+		      == CPP_OPEN_SQUARE)
 	      && !cp_parser_nth_token_starts_template_argument_list_p
 		  (parser, 2))
 	    break;
@@ -7127,6 +7197,12 @@  cp_parser_nested_name_specifier_opt (cp_parser *parser,
                                        check_dependency_p,
                                        type_p,
                                        is_declaration);
+
+      /* "T...[constant-expression]" is a C++26 pack-index-specifier.  */
+      if (cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS)
+	  && cp_lexer_nth_token_is (parser->lexer, 2, CPP_OPEN_SQUARE))
+      new_scope = cp_parser_pack_index (parser, new_scope);
+
       /* Look for the `::' token.  */
       cp_parser_require (parser, CPP_SCOPE, RT_SCOPE);
 
@@ -17410,6 +17486,11 @@  cp_parser_decltype_expr (cp_parser *parser,
       /* Parse a full expression.  */
       expr = cp_parser_expression (parser, /*pidk=*/NULL, /*cast_p=*/false,
 				   /*decltype_p=*/true);
+      /* A pack indexing is an id-expression.  cp_parser_expression does not
+	 tell us if the pack indexing was wrapped in (), so we use the
+	 PACK_INDEX_PARENTHESIZED_P flag to track that.  */
+      if (PACK_INDEX_P (expr))
+	id_expression_or_member_access_p = true;
     }
 
   return expr;
@@ -17938,13 +18019,17 @@  cp_parser_mem_initializer (cp_parser* parser)
 /* Parse a mem-initializer-id.
 
    mem-initializer-id:
-     :: [opt] nested-name-specifier [opt] class-name
-     decltype-specifier (C++11)
+     class-or-decltype
      identifier
 
+  class-or-decltype:
+    nested-name-specifier [opt] type-name
+    nested-name-specifier template simple-template-id
+    computed-type-specifier
+
    Returns a TYPE indicating the class to be initialized for the first
-   production (and the second in C++11).  Returns an IDENTIFIER_NODE
-   indicating the data member to be initialized for the last production.  */
+   production.  Returns an IDENTIFIER_NODE indicating the data member to
+   be initialized for the second production.  */
 
 static tree
 cp_parser_mem_initializer_id (cp_parser* parser)
@@ -18015,10 +18100,16 @@  cp_parser_mem_initializer_id (cp_parser* parser)
 			       /*class_head_p=*/false,
 			       /*is_declaration=*/true);
   /* If we found one, we're done.  */
-  if (cp_parser_parse_definitely (parser))
-    return id;
-  /* Otherwise, look for an ordinary identifier.  */
-  return cp_parser_identifier (parser);
+  if (!cp_parser_parse_definitely (parser))
+    /* Otherwise, look for an ordinary identifier.  */
+    id = cp_parser_identifier (parser);
+
+  /* ": T...[N]" is a C++26 pack-index-specifier.  */
+  if (cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS)
+      && cp_lexer_nth_token_is (parser->lexer, 2, CPP_OPEN_SQUARE))
+    id = cp_parser_pack_index (parser, id);
+
+  return id;
 }
 
 /* Overloading [gram.over] */
@@ -20436,11 +20527,11 @@  cp_parser_type_specifier (cp_parser* parser,
    C++11 Extension:
 
    simple-type-specifier:
-     auto
-     decltype ( expression )
      char16_t
      char32_t
      __underlying_type ( type-id )
+     computed-type-specifier
+     placeholder-type-specifier
 
    C++17 extension:
 
@@ -20822,6 +20913,13 @@  cp_parser_simple_type_specifier (cp_parser* parser,
 	    type = NULL_TREE;
 	}
 
+      /* "T...[constant-expression]" is a C++26 pack-index-specifier.  */
+      if (type
+	  && type != error_mark_node
+	  && cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS)
+	  && cp_lexer_nth_token_is (parser->lexer, 2, CPP_OPEN_SQUARE))
+	type = cp_parser_pack_index (parser, type);
+
       if (!type && flag_concepts && decl_specs)
 	{
 	  /* Try for a type-constraint with template arguments.  We check
@@ -29103,12 +29201,21 @@  cp_parser_base_clause (cp_parser* parser)
 /* Parse a base-specifier.
 
    base-specifier:
-     attribute-specifier-seq [opt] :: [opt] nested-name-specifier [opt]
-       class-name
-     attribute-specifier-seq [opt] virtual access-specifier [opt] :: [opt]
-       nested-name-specifier [opt] class-name
-     attribute-specifier-seq [opt] access-specifier virtual [opt] :: [opt]
-       nested-name-specifier [opt] class-name
+     attribute-specifier-seq [opt] class-or-decltype
+     attribute-specifier-seq [opt] virtual access-specifier [opt]
+       class-or-decltype
+     attribute-specifier-seq [opt] access-specifier virtual [opt]
+       class-or-decltype
+
+   class-or-decltype:
+     nested-name-specifier [opt] type-name
+     nested-name-specifier template simple-template-id
+     computed-type-specifier
+
+   access-specifier:
+     private
+     protected
+     public
 
    Returns a TREE_LIST.  The TREE_PURPOSE will be one of
    ACCESS_{DEFAULT,PUBLIC,PROTECTED,PRIVATE}_[VIRTUAL]_NODE to
@@ -29237,6 +29344,10 @@  cp_parser_base_specifier (cp_parser* parser)
 				   /*class_head_p=*/false,
 				   /*is_declaration=*/true);
       type = TREE_TYPE (type);
+      /* ": T...[constant-expression]" is a C++26 pack-index-specifier.  */
+      if (cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS)
+	  && cp_lexer_nth_token_is (parser->lexer, 2, CPP_OPEN_SQUARE))
+	type = cp_parser_pack_index (parser, type);
     }
 
   if (type == error_mark_node)
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index 15d6d82f32f..aae8606dd6e 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -1789,6 +1789,11 @@  iterative_hash_template_arg (tree arg, hashval_t val)
       val = iterative_hash_template_arg (PACK_EXPANSION_PATTERN (arg), val);
       return iterative_hash_template_arg (PACK_EXPANSION_EXTRA_ARGS (arg), val);
 
+    case PACK_INDEX_TYPE:
+    case PACK_INDEX_EXPR:
+      val = iterative_hash_template_arg (PACK_INDEX_PACK (arg), val);
+      return iterative_hash_template_arg (PACK_INDEX_INDEX (arg), val);
+
     case TYPE_ARGUMENT_PACK:
     case NONTYPE_ARGUMENT_PACK:
       return iterative_hash_template_arg (ARGUMENT_PACK_ARGS (arg), val);
@@ -4031,6 +4036,15 @@  find_parameter_packs_r (tree *tp, int *walk_subtrees, void* data)
       *walk_subtrees = 0;
       return NULL_TREE;
 
+    case PACK_INDEX_TYPE:
+    case PACK_INDEX_EXPR:
+      /* We can have an expansion of an expansion, such as "Ts...[Is]...",
+	 so do look into the index.  */
+      cp_walk_tree (&PACK_INDEX_INDEX (t), &find_parameter_packs_r, ppd,
+		    ppd->visited);
+      *walk_subtrees = 0;
+      return NULL_TREE;
+
     case INTEGER_TYPE:
       cp_walk_tree (&TYPE_MAX_VALUE (t), &find_parameter_packs_r,
 		    ppd, ppd->visited);
@@ -4257,6 +4271,21 @@  make_pack_expansion (tree arg, tsubst_flags_t complain)
   return result;
 }
 
+/* Create a PACK_INDEX_* using the pack expansion PACK and index INDEX.  */
+
+tree
+make_pack_index (tree pack, tree index)
+{
+  tree t = (TREE_CODE (pack) == TYPE_PACK_EXPANSION
+	    ? cxx_make_type (PACK_INDEX_TYPE)
+	    : make_node (PACK_INDEX_EXPR));
+  PACK_INDEX_PACK (t) = pack;
+  PACK_INDEX_INDEX (t) = index;
+  if (TREE_CODE (t) == PACK_INDEX_TYPE)
+    SET_TYPE_STRUCTURAL_EQUALITY (t);
+  return t;
+}
+
 /* Checks T for any "bare" parameter packs, which have not yet been
    expanded, and issues an error if any are found. This operation can
    only be done on full expressions or types (e.g., an expression
@@ -13645,11 +13674,12 @@  add_extra_args (tree extra, tree args, tsubst_flags_t complain, tree in_decl)
   return args;
 }
 
-/* Substitute ARGS into T, which is an pack expansion
-   (i.e. TYPE_PACK_EXPANSION or EXPR_PACK_EXPANSION). Returns a
+/* Substitute ARGS into T, which is a pack expansion
+   (i.e. TYPE_PACK_EXPANSION or EXPR_PACK_EXPANSION).  Returns a
    TREE_VEC with the substituted arguments, a PACK_EXPANSION_* node
    (if only a partial substitution could be performed) or
    ERROR_MARK_NODE if there was an error.  */
+
 tree
 tsubst_pack_expansion (tree t, tree args, tsubst_flags_t complain,
 		       tree in_decl)
@@ -13950,6 +13980,26 @@  tsubst_pack_expansion (tree t, tree args, tsubst_flags_t complain,
   return result;
 }
 
+/* Substitute ARGS into T, which is a pack index (i.e., PACK_INDEX_TYPE or
+   PACK_INDEX_EXPR).  Returns a single type or expression, a PACK_INDEX_*
+   node if only a partial substitution could be performed, or ERROR_MARK_NODE
+   if there was an error.  */
+
+tree
+tsubst_pack_index (tree t, tree args, tsubst_flags_t complain, tree in_decl)
+{
+  tree index = tsubst_expr (PACK_INDEX_INDEX (t), args, complain, in_decl);
+  if (value_dependent_expression_p (index))
+    return t;
+  tree pack = tsubst_pack_expansion (PACK_INDEX_PACK (t), args, complain,
+				     in_decl);
+  if (TREE_CODE (pack) == TREE_VEC)
+   return pack_index_element (index, pack, PACK_INDEX_PARENTHESIZED_P (t),
+			      complain);
+  else
+    return t;
+}
+
 /* Make an argument pack out of the TREE_VEC VEC.  */
 
 static tree
@@ -16338,7 +16388,8 @@  tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
       && code != TEMPLATE_PARM_INDEX
       && code != IDENTIFIER_NODE
       && code != FUNCTION_TYPE
-      && code != METHOD_TYPE)
+      && code != METHOD_TYPE
+      && code != PACK_INDEX_TYPE)
     type = tsubst (type, args, complain, in_decl);
   if (type == error_mark_node)
     return error_mark_node;
@@ -16898,9 +16949,15 @@  tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
 	    ctx = tsubst_pack_expansion (ctx, args,
 					 complain | tf_qualifying_scope,
 					 in_decl);
-	    if (ctx == error_mark_node
-		|| TREE_VEC_LENGTH (ctx) > 1)
+	    if (ctx == error_mark_node)
 	      return error_mark_node;
+	    if (TREE_VEC_LENGTH (ctx) > 1)
+		{
+		  if (complain & tf_error)
+		    error ("%qD expanded to more than one element",
+			   TYPENAME_TYPE_FULLNAME (t));
+		  return error_mark_node;
+		}
 	    if (TREE_VEC_LENGTH (ctx) == 0)
 	      {
 		if (complain & tf_error)
@@ -17042,12 +17099,17 @@  tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
 	else
 	  {
 	    bool id = DECLTYPE_TYPE_ID_EXPR_OR_MEMBER_ACCESS_P (t);
-	    if (id && TREE_CODE (DECLTYPE_TYPE_EXPR (t)) == BIT_NOT_EXPR
-		&& EXPR_P (type))
-	      /* In a template ~id could be either a complement expression
-		 or an unqualified-id naming a destructor; if instantiating
-		 it produces an expression, it's not an id-expression or
-		 member access.  */
+	    tree op = DECLTYPE_TYPE_EXPR (t);
+	    if (id
+		/* In a template ~id could be either a complement expression
+		   or an unqualified-id naming a destructor; if instantiating
+		   it produces an expression, it's not an id-expression or
+		   member access.  */
+		&& ((TREE_CODE (op) == BIT_NOT_EXPR && EXPR_P (type))
+		    /* If the pack indexing was wrapped in (), it's not an
+		       id-expression, either.  */
+		    || (PACK_INDEX_P (op)
+			&& PACK_INDEX_PARENTHESIZED_P (op))))
 	      id = false;
 	    type = finish_decltype_type (type, id, complain);
 	  }
@@ -17075,6 +17137,9 @@  tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
     case NONTYPE_ARGUMENT_PACK:
       return tsubst_argument_pack (t, args, complain, in_decl);
 
+    case PACK_INDEX_TYPE:
+      return tsubst_pack_index (t, args, complain, in_decl);
+
     case VOID_CST:
     case INTEGER_CST:
     case REAL_CST:
@@ -21776,6 +21841,9 @@  tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl)
 	RETURN (r);
       }
 
+    case PACK_INDEX_EXPR:
+    RETURN (tsubst_pack_index (t, args, complain, in_decl));
+
     case EXPR_PACK_EXPANSION:
       error ("invalid use of pack expansion expression");
       RETURN (error_mark_node);
@@ -28116,8 +28184,9 @@  dependent_type_p_r (tree type)
     }
 
   /* All TYPE_PACK_EXPANSIONs are dependent, because parameter packs must
-     be template parameters.  */
-  if (TREE_CODE (type) == TYPE_PACK_EXPANSION)
+     be template parameters.  This includes pack-index-specifiers.  */
+  if (TREE_CODE (type) == TYPE_PACK_EXPANSION
+      || TREE_CODE (type) == PACK_INDEX_TYPE)
     return true;
 
   if (TREE_CODE (type) == DEPENDENT_OPERATOR_TYPE)
@@ -28740,8 +28809,10 @@  type_dependent_expression_p (tree expression)
       && uses_outer_template_parms_in_constraints (expression))
     return true;
 
-  /* Always dependent, on the number of arguments if nothing else.  */
-  if (TREE_CODE (expression) == EXPR_PACK_EXPANSION)
+  /* Always dependent, on the number of arguments if nothing else.  This
+     includes pack-index-expressions.  */
+  if (TREE_CODE (expression) == EXPR_PACK_EXPANSION
+      || TREE_CODE (expression) == PACK_INDEX_EXPR)
     return true;
 
   if (TREE_TYPE (expression) == unknown_type_node)
diff --git a/gcc/cp/ptree.cc b/gcc/cp/ptree.cc
index 95c7a69e27c..74152c9476d 100644
--- a/gcc/cp/ptree.cc
+++ b/gcc/cp/ptree.cc
@@ -193,6 +193,11 @@  cxx_print_type (FILE *file, tree node, int indent)
       print_node (file, "args", PACK_EXPANSION_EXTRA_ARGS (node), indent + 4);
       return;
 
+    case PACK_INDEX_TYPE:
+      print_node (file, "pack", PACK_INDEX_PACK (node), indent + 4);
+      print_node (file, "index", PACK_INDEX_INDEX (node), indent + 4);
+      return;
+
     default:
       return;
     }
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index ab8614e376d..0b7cd514742 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -2453,6 +2453,8 @@  finish_parenthesized_expr (cp_expr expr)
   tree stripped_expr = tree_strip_any_location_wrapper (expr);
   if (TREE_CODE (stripped_expr) == STRING_CST)
     PAREN_STRING_LITERAL_P (stripped_expr) = 1;
+  else if (PACK_INDEX_P (stripped_expr))
+    PACK_INDEX_PARENTHESIZED_P (stripped_expr) = true;
 
   expr = cp_expr (force_paren_expr (expr), expr.get_location ());
 
@@ -4848,24 +4850,38 @@  finish_type_pack_element (tree idx, tree types, tsubst_flags_t complain)
   if (TREE_CODE (idx) != INTEGER_CST || !INTEGRAL_TYPE_P (TREE_TYPE (idx)))
     {
       if (complain & tf_error)
-	error ("%<__type_pack_element%> index is not an integral constant");
+	error ("pack index is not an integral constant");
       return error_mark_node;
     }
   if (tree_int_cst_sgn (idx) < 0)
     {
       if (complain & tf_error)
-	error ("%<__type_pack_element%> index is negative");
+	error ("pack index is negative");
       return error_mark_node;
     }
   if (wi::to_widest (idx) >= TREE_VEC_LENGTH (types))
     {
       if (complain & tf_error)
-	error ("%<__type_pack_element%> index is out of range");
+	error ("pack index is out of range");
       return error_mark_node;
     }
   return TREE_VEC_ELT (types, tree_to_shwi (idx));
 }
 
+/* In a pack-index T...[N], return the element at index IDX within TYPES.
+   PARENTHESIZED_P is true iff the pack index was wrapped in ().  */
+
+tree
+pack_index_element (tree idx, tree types, bool parenthesized_p,
+		    tsubst_flags_t complain)
+{
+  tree r = finish_type_pack_element (idx, types, complain);
+  if (parenthesized_p)
+    /* For the benefit of decltype(auto).  */
+    r = force_paren_expr (r);
+  return r;
+}
+
 /* Implement the __direct_bases keyword: Return the direct base classes
    of type.  */
 
diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc
index 911260685f1..53fcbc7273e 100644
--- a/gcc/cp/tree.cc
+++ b/gcc/cp/tree.cc
@@ -4245,6 +4245,15 @@  cp_tree_equal (tree t1, tree t2)
 	return false;
       return true;
 
+    case PACK_INDEX_EXPR:
+      if (!cp_tree_equal (PACK_INDEX_PACK (t1),
+			  PACK_INDEX_PACK (t2)))
+	return false;
+      if (!cp_tree_equal (PACK_INDEX_INDEX (t1),
+			  PACK_INDEX_INDEX (t2)))
+	return false;
+      return true;
+
     case COMPONENT_REF:
       /* If we're comparing contract conditions of overrides, member references
 	 compare equal if they designate the same member.  */
@@ -5585,6 +5594,13 @@  cp_walk_subtrees (tree *tp, int *walk_subtrees_p, walk_tree_fn func,
       *walk_subtrees_p = 0;
       break;
 
+    case PACK_INDEX_TYPE:
+    case PACK_INDEX_EXPR:
+      WALK_SUBTREE (PACK_INDEX_PACK (t));
+      WALK_SUBTREE (PACK_INDEX_INDEX (t));
+      *walk_subtrees_p = 0;
+      break;
+
     case CAST_EXPR:
     case REINTERPRET_CAST_EXPR:
     case STATIC_CAST_EXPR:
diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc
index 6ce1bb61fe7..06c632b9ee2 100644
--- a/gcc/cp/typeck.cc
+++ b/gcc/cp/typeck.cc
@@ -1623,6 +1623,12 @@  structural_comptypes (tree t1, tree t2, int strict)
 	      && comp_template_args (PACK_EXPANSION_EXTRA_ARGS (t1),
 				     PACK_EXPANSION_EXTRA_ARGS (t2)));
 
+    case PACK_INDEX_TYPE:
+      return (cp_tree_equal (PACK_INDEX_PACK (t1),
+			     PACK_INDEX_PACK (t2))
+	      && cp_tree_equal (PACK_INDEX_INDEX (t1),
+				PACK_INDEX_INDEX (t2)));
+
     case DECLTYPE_TYPE:
       if (DECLTYPE_TYPE_ID_EXPR_OR_MEMBER_ACCESS_P (t1)
           != DECLTYPE_TYPE_ID_EXPR_OR_MEMBER_ACCESS_P (t2))
diff --git a/gcc/testsuite/g++.dg/cpp26/pack-indexing1.C b/gcc/testsuite/g++.dg/cpp26/pack-indexing1.C
new file mode 100644
index 00000000000..e8c7a90ae58
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp26/pack-indexing1.C
@@ -0,0 +1,102 @@ 
+// P2662R3 - Pack Indexing
+// PR c++/113798
+// { dg-do compile { target c++17 } }
+// { dg-options "" }
+
+template<class, class> struct same_type;
+template<class T> struct same_type<T, T> {};
+
+template<int I, typename... Ts>
+using Type = Ts...[I]; // { dg-warning "pack indexing only available with" "" { target c++23_down } }
+
+template<int I, auto... Ts>
+constexpr auto Var = Ts...[I]; // { dg-warning "pack indexing only available with" "" { target c++23_down } }
+
+template <int I, auto...Ts>
+int
+foo ()
+{
+  return Ts...[I]; // { dg-warning "pack indexing only available with" "" { target c++23_down } }
+}
+
+template<typename... Ts>
+struct S {
+  Ts...[0] a; // { dg-warning "pack indexing only available with" "" { target c++23_down } }
+#if __cpp_concepts >= 201907L
+  void foo (auto... Vs) {
+    decltype(Vs...[1]) d1 = Vs...[1]; // { dg-warning "pack indexing only available with" "" { target { c++20 && c++23_down } } }
+  }
+#endif
+};
+
+int
+g ()
+{
+  using U = Type<1, char, int, float>;
+  using U = int;
+
+  constexpr auto V = Var<2, 0, 1, 42>;
+  static_assert (V == 42);
+
+  U r = foo<2, 0, 1, 42>();
+
+  return r;
+}
+
+void
+fn1 ()
+{
+  int i = 0;
+  [&i](auto... pack) {
+    // type is int
+    decltype(pack...[0]) x5 = 42; // { dg-warning "pack indexing only available with" "" { target c++23_down } }
+    // type is int&
+    [[maybe_unused]] decltype((pack...[0])) x6 = i; // { dg-warning "pack indexing only available with" "" { target c++23_down } }
+  }(0);
+}
+
+#if __cpp_concepts >= 201907L
+int
+bar (auto... pack)
+{
+  (void) pack...[0]; // { dg-warning "pack indexing only available with" "" { target { c++20 && c++23_down } } }
+  int x = pack...[0]; // { dg-warning "pack indexing only available with" "" { target { c++20 && c++23_down } } }
+  return x;
+}
+#endif
+
+template<auto...pack>
+void
+fn2 ()
+{
+  [[maybe_unused]] decltype(pack...[0]) x1; // { dg-warning "pack indexing only available with" "" { target c++23_down } }
+  [[maybe_unused]] decltype((pack...[0])) x2; // { dg-warning "pack indexing only available with" "" { target c++23_down } }
+  same_type<decltype(x1), int>();
+  same_type<decltype(x2), int>();
+}
+
+template<typename... T>
+void
+fn3 (int p)
+{
+  T...[0] a = p; // { dg-warning "pack indexing only available with" "" { target c++23_down } }
+  (T...[0])(a);  // { dg-warning "pack indexing only available with" "" { target c++23_down } }
+}
+
+template<int... Is>
+void fn4 ()
+{
+  same_type<decltype(Is...[0]), int>();	// { dg-warning "pack indexing only available with" "" { target c++23_down } }
+  same_type<decltype((Is...[0])), int>(); // { dg-warning "pack indexing only available with" "" { target c++23_down } }
+}
+
+void
+g3 ()
+{
+  fn2<0>();
+#if __cpp_concepts >= 201907L
+  bar (0);
+#endif
+  S<int> s;
+  fn4<0, 1, 2>();
+}
diff --git a/gcc/testsuite/g++.dg/cpp26/pack-indexing2.C b/gcc/testsuite/g++.dg/cpp26/pack-indexing2.C
new file mode 100644
index 00000000000..ec32527ed80
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp26/pack-indexing2.C
@@ -0,0 +1,111 @@ 
+// P2662R3 - Pack Indexing
+// PR c++/113798
+// { dg-do compile { target c++26 } }
+// Test invalid cases.
+
+template<int I, typename... Ts>
+using Type = Typo...[I]; // { dg-error "does not name a type" }
+
+template<int I, auto... Ts>
+constexpr auto Var = Typo...[I]; // { dg-error "no parameter packs" }
+
+template<typename... Ts>
+void foo(Ts...[]);  // { dg-error "pack index missing" }
+
+template <typename... Ts>
+void f(Ts...[1]);
+
+template<int... N>
+int f2 (X... [N]); // { dg-error "contains no parameter packs" }
+
+struct X;
+
+template<typename T>
+struct TX;
+
+template <typename T, auto V, template<typename> typename Tp>
+void
+bad (int i)
+{
+  i...[0];  // { dg-error "no parameter packs" }
+  V...[0];  // { dg-error "no parameter packs" }
+  X...[0] x;  // { dg-error "no parameter packs" }
+  T...[0] t;  // { dg-error "no parameter packs" }
+  Tp...[0] tp;  // { dg-error "expected" }
+
+  X...[0] xarr[1];  // { dg-error "no parameter packs" }
+  T...[0] tarr[1];  // { dg-error "no parameter packs" }
+  Tp...[0] tparr[1];  // { dg-error "expected" }
+}
+
+template<int N>
+int
+getT (auto... Ts)
+{
+  return Ts...[N]; // { dg-error "pack index is out of range" }
+}
+
+template<int N>
+int
+getT2 (auto... Ts)
+{
+  return Ts...[N]; // { dg-error "pack index is negative" }
+}
+
+template<auto N, typename... Ts>
+void
+badtype ()
+{
+  Ts...[N] t; // { dg-error "pack index is out of range" }
+}
+
+template<auto N, typename... Ts>
+void
+badtype2 ()
+{
+  Ts...[N] t; // { dg-error "pack index is negative" }
+}
+
+int nonconst () { return 42; }
+
+template<typename... Ts>
+void
+badindex ()
+{
+  Ts...[nonconst ()] t; // { dg-error "pack index is not an integral constant" }
+  // { dg-error "non-.constexpr. function" "" { target *-*-* } .-1 }
+}
+
+template<typename... Ts>
+struct broken {
+  Ts...1;  // { dg-error "expected" }
+  Ts...[;  // { dg-error "invalid" }
+  Ts...[1;  // { dg-error "invalid" }
+  Ts...[];  // { dg-error "pack index missing" }
+
+  void foo (auto...Vs) {
+    decltype(Vs...[1]) d1 = Vs...[]; // { dg-error "pack index missing" }
+    decltype(Vs...[1]) d2 = Vs...[; // { dg-error "expected" }
+  }
+};
+
+int main()
+{
+   // void f<int, double>(int [1], double [1])
+  f<int, double>(nullptr, nullptr); // { dg-error "no matching function" }
+  bad<int, 0, TX>(42);
+
+  getT<0>(); // { dg-message "required from here" }
+  getT<1>();  // { dg-message "required from here" }
+  getT2<-1>();  // { dg-message "required from here" }
+
+  badtype<0>(); // { dg-message "required from here" }
+  badtype<1, int>(); // { dg-message "required from here" }
+  badtype2<-1>(); // { dg-message "required from here" }
+  badtype2<-1, int>(); // { dg-message "required from here" }
+
+  badindex<int, int, int>();
+
+  bool b = nothere...[0]; // { dg-error "no parameter packs" }
+  using E = nothere...[0]; // { dg-error "does not name a type" }
+}
diff --git a/gcc/testsuite/g++.dg/cpp26/pack-indexing3.C b/gcc/testsuite/g++.dg/cpp26/pack-indexing3.C
new file mode 100644
index 00000000000..8c10b307f3a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp26/pack-indexing3.C
@@ -0,0 +1,41 @@ 
+// P2662R3 - Pack Indexing
+// PR c++/113798
+// { dg-do compile { target c++26 } }
+// From LLVM's cxx2c-pack-indexing.cpp.
+
+template<typename...>
+struct X { };
+
+template<typename... T>
+requires requires(T...[0]) { {T...[0](0)}; }
+struct S : T...[1] {
+  [[maybe_unused]] T...[1] base = {};
+  using foo = T...[1];
+  S() : T...[1]() { }
+  X<T...[0]> x;
+  const T...[0] f(T...[0]&& parm) noexcept((T...[0])0) {
+    T...[0] (*test)(const volatile T...[0]**);
+    thread_local T...[0] d;
+    [[maybe_unused]] T...[0] a = parm;
+    auto ptr = new T...[0](0);
+    (*ptr).~T...[0]();
+    return T...[0](0);
+    typename T...[1]::foo b = 0;
+    T...[1]::i = 0;
+    return (T...[0])(a);
+    new T...[0];
+    [[maybe_unused]] auto l = []<T...[0]>(T...[0][1]) -> T...[0]{ return {}; };
+    [[maybe_unused]] auto _ = l.template operator()<T...[0]{}>({0});
+  }
+  operator T...[0]() const { }
+};
+
+struct base {
+    using foo = int;
+    static inline int i = 42;
+};
+
+int main()
+{
+  S<int, base>().f(0);
+}
diff --git a/gcc/testsuite/g++.dg/cpp26/pack-indexing4.C b/gcc/testsuite/g++.dg/cpp26/pack-indexing4.C
new file mode 100644
index 00000000000..8decf3064bc
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp26/pack-indexing4.C
@@ -0,0 +1,65 @@ 
+// P2662R3 - Pack Indexing
+// PR c++/113798
+// { dg-do compile { target c++26 } }
+// From LLVM's cxx2c-pack-indexing.cpp.
+
+template <class, class>
+constexpr bool is_same = false;
+template <class T>
+constexpr bool is_same<T, T> = true;
+
+template <typename T>
+constexpr bool
+f (auto&&... p)
+{
+  return is_same<T, decltype(p...[0])>;
+}
+
+void
+g ()
+{
+    int a = 0;
+    const int b = 0;
+    static_assert(f<int&&>(0));
+    static_assert(f<int&>(a));
+    static_assert(f<const int&>(b));
+}
+
+template<auto... p>
+struct A {
+  enum E {
+    x = p...[0]
+  };
+};
+static_assert(A<42>::x == 42);
+
+struct S { };
+template<auto... p>
+constexpr auto constant_initializer = p...[0];
+constexpr auto InitOk = constant_initializer<S{}>;
+
+consteval int evaluate(auto... p) {
+    return p...[0];
+}
+constexpr int x = evaluate(42, S{});
+static_assert(x == 42);
+
+template <auto... Is>
+struct IL{};
+
+template <typename... Ts>
+struct TL{};
+
+template <typename Tl, typename Il>
+struct SpliceImpl;
+
+template <typename... Ts, auto... Is>
+struct SpliceImpl<TL<Ts...>, IL<Is...>>
+{
+    using type = TL<Ts...[Is]...>;
+};
+
+template <typename Tl, typename Il>
+using Splice = typename SpliceImpl<Tl, Il>::type;
+using type = Splice<TL<char, short, long, double>, IL<1, 2>>;
+static_assert(is_same<type, TL<short, long>>);
diff --git a/gcc/testsuite/g++.dg/cpp26/pack-indexing5.C b/gcc/testsuite/g++.dg/cpp26/pack-indexing5.C
new file mode 100644
index 00000000000..901956e2dae
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp26/pack-indexing5.C
@@ -0,0 +1,41 @@ 
+// P2662R3 - Pack Indexing
+// PR c++/113798
+// { dg-do compile { target c++26 } }
+
+template<class, class> struct same_type;
+template<class T> struct same_type<T, T> {};
+
+void
+fn1 (auto... Ts)
+{
+  same_type<decltype(Ts...[0]), int>();
+  same_type<decltype((Ts...[0])), int&>();
+  same_type<decltype(Ts...[1]), unsigned int>();
+  same_type<decltype((Ts...[1])), unsigned int&>();
+}
+
+template<auto... Is>
+void
+fn2 ()
+{
+  same_type<decltype(Is...[0]), int>();
+  same_type<decltype((Is...[0])), int>();
+  same_type<decltype(Is...[1]), unsigned int>();
+  same_type<decltype((Is...[1])), unsigned int>();
+  same_type<decltype(Is...[2]), double>();
+  same_type<decltype((Is...[2])), double>();
+  same_type<decltype(Is...[3]), float>();
+  same_type<decltype((Is...[3])), float>();
+  same_type<decltype(Is...[4]), unsigned char>();
+  same_type<decltype((Is...[4])), unsigned char>();
+}
+
+static constexpr unsigned char c = 'A';
+
+void
+g ()
+{
+  int i = 42;
+  fn1 (i, 42u);
+  fn2<0, 1u, 2.0, 3.f, c>();
+}
diff --git a/gcc/testsuite/g++.dg/cpp26/pack-indexing6.C b/gcc/testsuite/g++.dg/cpp26/pack-indexing6.C
new file mode 100644
index 00000000000..9af88765254
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp26/pack-indexing6.C
@@ -0,0 +1,51 @@ 
+// P2662R3 - Pack Indexing
+// PR c++/113798
+// { dg-do compile { target c++26 } }
+
+template<class, class> struct same_type;
+template<class T> struct same_type<T, T> {};
+
+void
+fn1 (auto... Ts)
+{
+  decltype(auto) a1 = Ts...[0];
+  same_type<decltype(a1), int>();
+  decltype(auto) a2 = (Ts...[0]);
+  same_type<decltype(a2), int&>();
+}
+
+template<auto... Is>
+void
+fn2 ()
+{
+  decltype(auto) a1 = Is...[0];
+  same_type<decltype(a1), int>();
+  decltype(auto) a2 = (Is...[0]);
+  same_type<decltype(a2), int>();
+  decltype(auto) a3 = Is...[1];
+  same_type<decltype(a3), unsigned int>();
+  decltype(auto) a4 = (Is...[1]);
+  same_type<decltype(a4), unsigned int>();
+  decltype(auto) a5 = Is...[2];
+  same_type<decltype(a5), double>();
+  decltype(auto) a6 = (Is...[2]);
+  same_type<decltype(a6), double>();
+  decltype(auto) a7 = Is...[3];
+  same_type<decltype(a7), float>();
+  decltype(auto) a8 = (Is...[3]);
+  same_type<decltype(a8), float>();
+  decltype(auto) a9 = Is...[4];
+  same_type<decltype(a9), unsigned char>();
+  decltype(auto) a10 = (Is...[4]);
+  same_type<decltype(a10), unsigned char>();
+}
+
+static constexpr unsigned char c = 'A';
+
+void
+g ()
+{
+  int i = 42;
+  fn1 (i, 42u);
+  fn2<0, 1u, 2.0, 3.f, c>();
+}
diff --git a/gcc/testsuite/g++.dg/cpp26/pack-indexing7.C b/gcc/testsuite/g++.dg/cpp26/pack-indexing7.C
new file mode 100644
index 00000000000..06e2a3d0dc7
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp26/pack-indexing7.C
@@ -0,0 +1,16 @@ 
+// P2662R3 - Pack Indexing
+// PR c++/113798
+// { dg-do compile { target c++26 } }
+
+template <int I, auto...Ts>
+decltype(Ts...[I])
+foo ()			      // { dg-message "sorry, unimplemented: mangling" }
+{
+  return Ts...[I];
+}
+
+int
+g ()
+{
+  return foo<2, 0, 1, 42>();
+}
diff --git a/gcc/testsuite/g++.dg/cpp26/pack-indexing8.C b/gcc/testsuite/g++.dg/cpp26/pack-indexing8.C
new file mode 100644
index 00000000000..2b1b67c0841
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp26/pack-indexing8.C
@@ -0,0 +1,23 @@ 
+// P2662R3 - Pack Indexing
+// PR c++/113798
+// { dg-do run { target c++26 } }
+
+#include <initializer_list>
+
+template<typename... Ts>
+int
+g (auto... Is)
+{
+  std::initializer_list<Ts...[0]> l{ Is...[0], Is...[1], Is...[2], Is...[3], Is...[4] };
+  int sum = 0;
+  for (auto x : l)
+    sum += x;
+  return sum;
+}
+
+int
+main ()
+{
+  if (g<int> (1, 2, 3, 4, 5) != 15)
+    __builtin_abort ();
+}
diff --git a/gcc/testsuite/g++.dg/cpp26/pack-indexing9.C b/gcc/testsuite/g++.dg/cpp26/pack-indexing9.C
new file mode 100644
index 00000000000..468ae9dc4d1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp26/pack-indexing9.C
@@ -0,0 +1,27 @@ 
+// P2662R3 - Pack Indexing
+// PR c++/113798
+// { dg-do compile { target c++26 } }
+// From <https://github.com/itanium-cxx-abi/cxx-abi/issues/175>.
+
+template <class... T> struct tuple {
+  template <unsigned I> T...[I] get();  // { dg-message "sorry, unimplemented: mangling" }
+};
+
+int
+g ()
+{
+  tuple<int> t;
+  return t.get<0>();
+}
+
+template<typename T, typename U> concept C = true;
+template<typename ...T> struct A {
+    template<int I, typename ...U> void f(T...[I], U...[I]) requires C<T...[I], U...[I]>;  // { dg-message "sorry, unimplemented: mangling" }
+};
+
+void
+h ()
+{
+  A<char, int, double> a;
+  a.f<1, int, int, char>(1, 2);
+}
diff --git a/gcc/testsuite/g++.dg/modules/pack-index-1_a.C b/gcc/testsuite/g++.dg/modules/pack-index-1_a.C
new file mode 100644
index 00000000000..30ae189711c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/pack-index-1_a.C
@@ -0,0 +1,18 @@ 
+// { dg-module-do run }
+// { dg-additional-options { -std=c++26 -fmodules-ts } }
+
+export module packing1;
+// { dg-module-cmi "packing1" }
+
+export template<int I, typename... Ts>
+using Type = Ts...[I];
+
+export template<int I, auto... Ts>
+constexpr auto Var = Ts...[I];
+
+export template <int I, auto...Ts>
+int
+foo ()
+{
+  return Ts...[I];
+}
diff --git a/gcc/testsuite/g++.dg/modules/pack-index-1_b.C b/gcc/testsuite/g++.dg/modules/pack-index-1_b.C
new file mode 100644
index 00000000000..dce6f5774d3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/pack-index-1_b.C
@@ -0,0 +1,15 @@ 
+// { dg-additional-options { -std=c++26 -fmodules-ts } }
+
+import packing1;
+
+int
+main ()
+{
+  using U = Type<1, char, int, float>;
+  using U = int;
+
+  U r = foo<2, 0, 1, 42>();
+
+  constexpr auto V = Var<2, 0, 1, 42>;
+  static_assert (V == 42);
+}
diff --git a/libstdc++-v3/testsuite/20_util/tuple/element_access/get_neg.cc b/libstdc++-v3/testsuite/20_util/tuple/element_access/get_neg.cc
index 2346c29b80f..9e002f4d47a 100644
--- a/libstdc++-v3/testsuite/20_util/tuple/element_access/get_neg.cc
+++ b/libstdc++-v3/testsuite/20_util/tuple/element_access/get_neg.cc
@@ -61,4 +61,4 @@  test03()
 
 // { dg-error "tuple index must be in range" "" { target *-*-* } 0 }
 // { dg-prune-output "no type named 'type' in .*_Nth_type" }
-// { dg-prune-output "'__type_pack_element' index is out of range" }
+// { dg-prune-output "pack index is out of range" }