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

Reply via email to