On Tue, 22 Oct 2024, Marek Polacek wrote:

> Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?
> 
> -- >8 --
> This patch implements C++26 Pack Indexing, as described in
> <https://wg21.link/P2662R3>.
> 
> The issue discussing how to mangle pack indexes has not been resolved
> yet <https://github.com/itanium-cxx-abi/cxx-abi/issues/175> and I've
> made no attempt to address it so far.
> 
> Rather than introducing a new template code for a pack indexing, I'm
> adding a new operand to EXPR_PACK_EXPANSION to store the index; for
> TYPE_PACK_EXPANSION, I'm stashing the index into TYPE_VALUES_RAW.  This

What are the pros and cons of reusing TYPE/EXPR_PACK_EXPANSION instead
of creating two new tree codes for these operators (one of whose
operands would itself be a bare TYPE/EXPR_PACK_EXPANSION)?

I feel a little iffy at first glance about reusing these tree codes
since it muddles what "kind" of tree they are: currently they represent
a _vector_ or types/exprs (which is reflected by their tcc_exceptional
class), and with this approach they can now also represent a single
type/expr (despite their tcc_exceptional class), depending on whether
PACK_EXPANSION_INDEX is set.

At the same time, the pattern of a generic *_PACK_EXPANSION can be
anything whereas for these index operators we know it's always a single
bare pack, so we also don't need the full expressivity of
*_PACK_EXPANSION to represent these operators either.

Maybe it's the case that reusing these tree codes significantly
simplifies parts of the implementation?

> feature is akin to __type_pack_element, so they can share the element
> extraction part.
> 
> A pack indexing in a decltype proved to be a bit tricky; eventually,
> I've added PACK_EXPANSION_PARENTHESIZED_P -- while parsing, we can't
> really tell what it's going to expand to.
> 
> With this feature, it's valid to write something like
> 
>   using U = tmpl<Ts...[Is]...>;
> 
> where we first expand the template argument into
> 
>   Ts...[Is#0], Ts...[Is#1], ...
> 
> and then substitute each individual pack index.
> 
> I have no test for the module.cc code, that is just guesswork.
> 
>       PR c++/113798
> 
> gcc/cp/ChangeLog:
> 
>       * cp-tree.def (EXPR_PACK_EXPANSION): Add another operand.
>       * cp-tree.h (PACK_EXPANSION_INDEX): Define.
>       (PACK_EXPANSION_PARENTHESIZED_P): Define.
>       (pack_index_element): Declare.
>       * cxx-pretty-print.cc (cxx_pretty_printer::expression)
>       <case EXPR_PACK_EXPANSION>: Print PACK_EXPANSION_INDEX.
>       (cxx_pretty_printer::type_id) <case TYPE_PACK_EXPANSION>: Print
>       PACK_EXPANSION_INDEX.
>       * decl.cc (xref_basetypes): Set PACK_EXPANSION_INDEX.
>       * error.cc (dump_type): Print PACK_EXPANSION_INDEX.
>       * mangle.cc (write_type) <case TYPE_PACK_EXPANSION>: New comment.
>       * module.cc (trees_out::type_node): Stream PACK_EXPANSION_INDEX.
>       (trees_in::tree_node): Read in PACK_EXPANSION_INDEX.
>       * parser.cc (cp_parser_pack_index): New.
>       (cp_parser_primary_expression): Handle a pack-index-expression.
>       (cp_parser_unqualified_id): Handle a pack-index-specifier.
>       (cp_parser_nested_name_specifier_opt): Also check for
>       a pack-index-specifier.  Handle a pack-index-specifier.
>       (cp_parser_mem_initializer_id): Handle a pack-index-specifier.
>       (cp_parser_simple_type_specifier): Likewise.
>       (cp_parser_base_specifier): Likewise.
>       * pt.cc (iterative_hash_template_arg) <case TYPE_PACK_EXPANSION>: Also
>       hash PACK_EXPANSION_INDEX.
>       (find_parameter_packs_r) <case VAR_DECL>: A type with
>       PACK_EXPANSION_INDEX is not a bare parameter pack.
>       <case TYPE_PACK_EXPANSION>: Walk into PACK_EXPANSION_INDEXes.
>       (instantiate_class_template): Handle a pack-index-specifier.
>       (tsubst_pack_expansion): tsubst_expr the PACK_EXPANSION_INDEX.
>       If there was a PACK_EXPANSION_INDEX, pull out the element via
>       pack_index_element.
>       (tsubst) <case TYPENAME_TYPE>: Handle a pack-index-specifier.
>       <case DECLTYPE_TYPE>: For a PACK_EXPANSION_P, figure out if it should
>       be treated as an id-expression.
>       <case TYPE_PACK_EXPANSION>: Handle it if there is a
>       PACK_EXPANSION_INDEX.
>       (tsubst_stmt) <case EXPR_PACK_EXPANSION>: Likewise.
>       (tsubst_expr) <case EXPR_PACK_EXPANSION>: Likewise.
>       (tsubst_initializer_list): Handle a pack-index-specifier.
>       * ptree.cc (cxx_print_type) <case TYPE_PACK_EXPANSION>: Print
>       the PACK_EXPANSION_INDEX.
>       * semantics.cc (finish_parenthesized_expr): Maybe set
>       PACK_EXPANSION_PARENTHESIZED_P.
>       (finish_base_specifier): Check for a PACK_EXPANSION_P with
>       a PACK_EXPANSION_INDEX.
>       (get_vec_elt_checking): New, broken out of finish_type_pack_element.
>       (finish_type_pack_element): Call get_vec_elt_checking.
>       (pack_index_element): New.
>       * tree.cc (cp_build_qualified_type): Set PACK_EXPANSION_INDEX.
>       (cp_tree_equal) <case EXPR_PACK_EXPANSION>: Also compare the
>       PACK_EXPANSION_INDEXes.
>       (cp_walk_subtrees) <case TYPE_PACK_EXPANSION,
>       case EXPR_PACK_EXPANSION>: Walk the PACK_EXPANSION_INDEX.
>       * typeck.cc (structural_comptypes) <case TYPE_PACK_EXPANSION>: Also
>       compare the PACK_EXPANSION_INDEXes.
> 
> gcc/testsuite/ChangeLog:
> 
>       * g++.dg/cpp26/pack-indexing1.C: New test.
>       * g++.dg/cpp26/pack-indexing2.C: New test.
>       * g++.dg/cpp26/pack-indexing3.C: New test.
>       * g++.dg/cpp26/pack-indexing4.C: New test.
>       * g++.dg/cpp26/pack-indexing5.C: New test.
> ---
>  gcc/cp/cp-tree.def                          |   2 +-
>  gcc/cp/cp-tree.h                            |  13 ++
>  gcc/cp/cxx-pretty-print.cc                  |  12 ++
>  gcc/cp/decl.cc                              |   8 +-
>  gcc/cp/error.cc                             |   6 +
>  gcc/cp/mangle.cc                            |   2 +
>  gcc/cp/module.cc                            |   3 +
>  gcc/cp/parser.cc                            | 144 +++++++++++++++++---
>  gcc/cp/pt.cc                                |  90 +++++++++---
>  gcc/cp/ptree.cc                             |   1 +
>  gcc/cp/semantics.cc                         |  50 ++++++-
>  gcc/cp/tree.cc                              |  14 +-
>  gcc/cp/typeck.cc                            |   4 +-
>  gcc/testsuite/g++.dg/cpp26/pack-indexing1.C | 102 ++++++++++++++
>  gcc/testsuite/g++.dg/cpp26/pack-indexing2.C | 111 +++++++++++++++
>  gcc/testsuite/g++.dg/cpp26/pack-indexing3.C |  41 ++++++
>  gcc/testsuite/g++.dg/cpp26/pack-indexing4.C |  65 +++++++++
>  gcc/testsuite/g++.dg/cpp26/pack-indexing5.C |  41 ++++++
>  18 files changed, 662 insertions(+), 47 deletions(-)
>  create mode 100644 gcc/testsuite/g++.dg/cpp26/pack-indexing1.C
>  create mode 100644 gcc/testsuite/g++.dg/cpp26/pack-indexing2.C
>  create mode 100644 gcc/testsuite/g++.dg/cpp26/pack-indexing3.C
>  create mode 100644 gcc/testsuite/g++.dg/cpp26/pack-indexing4.C
>  create mode 100644 gcc/testsuite/g++.dg/cpp26/pack-indexing5.C
> 
> diff --git a/gcc/cp/cp-tree.def b/gcc/cp/cp-tree.def
> index 18f75108c7b..576765667d0 100644
> --- a/gcc/cp/cp-tree.def
> +++ b/gcc/cp/cp-tree.def
> @@ -395,7 +395,7 @@ DEFTREECODE (TYPE_PACK_EXPANSION, "type_pack_expansion", 
> tcc_type, 0)
>  
>     EXPR_PACK_EXPANSION plays precisely the same role as TYPE_PACK_EXPANSION,
>     but will be used for expressions.  */
> -DEFTREECODE (EXPR_PACK_EXPANSION, "expr_pack_expansion", tcc_expression, 3)
> +DEFTREECODE (EXPR_PACK_EXPANSION, "expr_pack_expansion", tcc_expression, 4)
>  
>  /* Selects the Ith parameter out of an argument pack. This node will
>     be used when instantiating pack expansions; see
> diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> index a44100a2bc4..12472d95247 100644
> --- a/gcc/cp/cp-tree.h
> +++ b/gcc/cp/cp-tree.h
> @@ -510,6 +510,7 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX];
>        OVL_LOOKUP_P (in OVERLOAD)
>        LOOKUP_FOUND_P (in RECORD_TYPE, UNION_TYPE, ENUMERAL_TYPE, 
> NAMESPACE_DECL)
>        FNDECL_MANIFESTLY_CONST_EVALUATED (in FUNCTION_DECL)
> +      PACK_EXPANSION_PARENTHESIZED_P (in *_PACK_EXPANSION)
>     5: IDENTIFIER_VIRTUAL_P (in IDENTIFIER_NODE)
>        FUNCTION_RVALUE_QUALIFIED (in FUNCTION_TYPE, METHOD_TYPE)
>        CALL_EXPR_REVERSE_ARGS (in CALL_EXPR, AGGR_INIT_EXPR)
> @@ -4025,6 +4026,12 @@ struct GTY(()) lang_decl {
>      ? &TYPE_MAX_VALUE_RAW (NODE)                     \
>      : &TREE_OPERAND ((NODE), 2))
>  
> +/* For a pack-index T...[N], the index N.  */
> +#define PACK_EXPANSION_INDEX(NODE) \
> +  *(TREE_CODE (PACK_EXPANSION_CHECK (NODE)) == TYPE_PACK_EXPANSION \
> +    ? &TYPE_VALUES_RAW (NODE)                        \
> +    : &TREE_OPERAND ((NODE), 3))
> +
>  /* True iff this pack expansion is within a function context.  */
>  #define PACK_EXPANSION_LOCAL_P(NODE) \
>    TREE_LANG_FLAG_0 (PACK_EXPANSION_CHECK (NODE))
> @@ -4042,6 +4049,11 @@ struct GTY(()) lang_decl {
>  #define PACK_EXPANSION_FORCE_EXTRA_ARGS_P(NODE) \
>    TREE_LANG_FLAG_3 (PACK_EXPANSION_CHECK (NODE))
>  
> +/* Indicates whether a pack expansion has been parenthesized.  Used for
> +   a pack expansion in a decltype.  */
> +#define PACK_EXPANSION_PARENTHESIZED_P(NODE) \
> +  TREE_LANG_FLAG_4 (PACK_EXPANSION_CHECK (NODE))
> +
>  /* True iff the wildcard can match a template parameter pack.  */
>  #define WILDCARD_PACK_P(NODE) TREE_LANG_FLAG_0 (NODE)
>  
> @@ -7906,6 +7918,7 @@ extern tree finish_underlying_type              (tree);
>  extern tree calculate_bases                     (tree, tsubst_flags_t);
>  extern tree finish_bases                        (tree, bool);
>  extern tree calculate_direct_bases              (tree, tsubst_flags_t);
> +extern tree pack_index_element                       (tree, tree, 
> tsubst_flags_t);
>  extern tree finish_offsetof                  (tree, tree, location_t);
>  extern void finish_decl_cleanup                      (tree, tree);
>  extern void finish_eh_cleanup                        (tree);
> diff --git a/gcc/cp/cxx-pretty-print.cc b/gcc/cp/cxx-pretty-print.cc
> index 41e6bdfdda5..47111195f72 100644
> --- a/gcc/cp/cxx-pretty-print.cc
> +++ b/gcc/cp/cxx-pretty-print.cc
> @@ -1216,6 +1216,12 @@ cxx_pretty_printer::expression (tree t)
>      case EXPR_PACK_EXPANSION:
>        expression (PACK_EXPANSION_PATTERN (t));
>        pp_cxx_ws_string (this, "...");
> +      if (PACK_EXPANSION_INDEX (t))
> +     {
> +       pp_cxx_left_bracket (this);
> +       expression (PACK_EXPANSION_INDEX (t));
> +       pp_cxx_right_bracket (this);
> +     }
>        break;
>  
>      case UNARY_LEFT_FOLD_EXPR:
> @@ -1916,6 +1922,12 @@ cxx_pretty_printer::type_id (tree t)
>      case TYPE_PACK_EXPANSION:
>        type_id (PACK_EXPANSION_PATTERN (t));
>        pp_cxx_ws_string (this, "...");
> +      if (PACK_EXPANSION_INDEX (t))
> +     {
> +       pp_cxx_left_bracket (this);
> +       expression (PACK_EXPANSION_INDEX (t));
> +       pp_cxx_right_bracket (this);
> +     }
>        break;
>  
>      case TYPE_ARGUMENT_PACK:
> diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
> index 7281818be8f..311f4ad6cd1 100644
> --- a/gcc/cp/decl.cc
> +++ b/gcc/cp/decl.cc
> @@ -17040,8 +17040,12 @@ xref_basetypes (tree ref, tree base_list)
>       }
>  
>        if (PACK_EXPANSION_P (TREE_VALUE (base_list)))
> -        /* Regenerate the pack expansion for the bases. */
> -        basetype = make_pack_expansion (basetype);
> +     {
> +       /* Regenerate the pack expansion for the bases. */
> +       basetype = make_pack_expansion (basetype);
> +       PACK_EXPANSION_INDEX (basetype)
> +         = PACK_EXPANSION_INDEX (TREE_VALUE (base_list));
> +     }
>  
>        TYPE_MARKED_P (basetype) = 1;
>  
> diff --git a/gcc/cp/error.cc b/gcc/cp/error.cc
> index 65f70c595cf..33f964a11bd 100644
> --- a/gcc/cp/error.cc
> +++ b/gcc/cp/error.cc
> @@ -818,6 +818,12 @@ dump_type (cxx_pretty_printer *pp, tree t, int flags)
>      case TYPE_PACK_EXPANSION:
>        dump_type (pp, PACK_EXPANSION_PATTERN (t), flags);
>        pp_cxx_ws_string (pp, "...");
> +      if (PACK_EXPANSION_INDEX (t))
> +     {
> +       pp_cxx_left_bracket (pp);
> +       dump_expr (pp, PACK_EXPANSION_INDEX (t), flags & ~TFF_EXPR_IN_PARENS);
> +       pp_cxx_right_bracket (pp);
> +     }
>        break;
>  
>      case TYPE_ARGUMENT_PACK:
> diff --git a/gcc/cp/mangle.cc b/gcc/cp/mangle.cc
> index 17988d69e1e..8c8a89897c9 100644
> --- a/gcc/cp/mangle.cc
> +++ b/gcc/cp/mangle.cc
> @@ -2605,6 +2605,8 @@ write_type (tree type)
>              case TYPE_PACK_EXPANSION:
>                write_string ("Dp");
>                write_type (PACK_EXPANSION_PATTERN (type));
> +           /* TODO: Mangle PACK_EXPANSION_INDEX
> +              <https://github.com/itanium-cxx-abi/cxx-abi/issues/175>  */
>                break;
>  
>              case DECLTYPE_TYPE:
> diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc
> index fd9b1d3bf2e..da3e2b71a66 100644
> --- a/gcc/cp/module.cc
> +++ b/gcc/cp/module.cc
> @@ -9207,6 +9207,7 @@ trees_out::type_node (tree type)
>       u (PACK_EXPANSION_LOCAL_P (type));
>        tree_node (PACK_EXPANSION_PARAMETER_PACKS (type));
>        tree_node (PACK_EXPANSION_EXTRA_ARGS (type));
> +      tree_node (PACK_EXPANSION_INDEX (type));
>        break;
>  
>      case TYPENAME_TYPE:
> @@ -9763,6 +9764,7 @@ trees_in::tree_node (bool is_use)
>             bool local = u ();
>             tree param_packs = tree_node ();
>             tree extra_args = tree_node ();
> +           tree index = tree_node ();
>             if (!get_overrun ())
>               {
>                 tree expn = cxx_make_type (TYPE_PACK_EXPANSION);
> @@ -9771,6 +9773,7 @@ trees_in::tree_node (bool is_use)
>                 PACK_EXPANSION_PARAMETER_PACKS (expn) = param_packs;
>                 PACK_EXPANSION_EXTRA_ARGS (expn) = extra_args;
>                 PACK_EXPANSION_LOCAL_P (expn) = local;
> +               PACK_EXPANSION_INDEX (expn) = index;
>                 res = expn;
>               }
>           }
> diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
> index 9d31a975dcf..7a054fb6c05 100644
> --- a/gcc/cp/parser.cc
> +++ b/gcc/cp/parser.cc
> @@ -5739,6 +5739,56 @@ cp_parser_fold_expression (cp_parser *parser, tree 
> expr1)
>    return finish_binary_fold_expr (loc, expr1, expr2, op);
>  }
>  
> +/*  Parse a pack-index-specifier:
> +
> +    pack-index-specifier:
> +      typedef-name ... [ constant-expression ]
> +
> +    or a pack-index-expression:
> +
> +    pack-index-expression:
> +      id-expression ... [ constant-expression ]
> +
> +    PACK is the parsed typedef-name or the id-expression.  Returns
> +    either an EXPR_PACK_EXPANSION or TYPE_PACK_EXPANSION.  */
> +
> +static tree
> +cp_parser_pack_index (cp_parser *parser, tree pack)
> +{
> +  if (cxx_dialect < cxx26)
> +    pedwarn (cp_lexer_peek_token (parser->lexer)->location,
> +          OPT_Wc__26_extensions, "pack indexing only available with "
> +          "%<-std=c++2c%> or %<-std=gnu++2c%>");
> +  /* Consume the '...' token.  */
> +  cp_lexer_consume_token (parser->lexer);
> +  /* Consume the '['.  */
> +  cp_lexer_consume_token (parser->lexer);
> +
> +  if (cp_lexer_next_token_is (parser->lexer, CPP_CLOSE_SQUARE))
> +    {
> +      error_at (cp_lexer_peek_token (parser->lexer)->location,
> +             "pack index missing");
> +      cp_lexer_consume_token (parser->lexer);
> +      return error_mark_node;
> +    }
> +
> +  tree expr = cp_parser_constant_expression (parser,
> +                                          /*allow_non_constant_p=*/false,
> +                                          /*non_constant_p=*/nullptr,
> +                                          /*strict_p=*/true);
> +  /* Consume the ']'.  */
> +  cp_parser_require (parser, CPP_CLOSE_SQUARE, RT_CLOSE_SQUARE);
> +
> +  if (TREE_CODE (pack) == TYPE_DECL)
> +    pack = TREE_TYPE (pack);
> +  pack = make_pack_expansion (pack);
> +  if (pack == error_mark_node)
> +    return error_mark_node;
> +  PACK_EXPANSION_INDEX (pack) = expr;
> +
> +  return pack;
> +}
> +
>  /* Parse a primary-expression.
>  
>     primary-expression:
> @@ -6368,6 +6418,12 @@ cp_parser_primary_expression (cp_parser *parser,
>         = make_location (caret_loc, start_loc, finish_loc);
>  
>       decl.set_location (combined_loc);
> +
> +     /* "T...[constant-expression]" is a C++26 pack-index-expression.  */
> +     if (cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS)
> +         && cp_lexer_nth_token_is (parser->lexer, 2, CPP_OPEN_SQUARE))
> +       decl = cp_parser_pack_index (parser, decl);
> +
>       return decl;
>        }
>  
> @@ -6411,6 +6467,7 @@ missing_template_diag (location_t loc, diagnostic_t 
> diag_kind = DK_WARNING)
>     id-expression:
>       unqualified-id
>       qualified-id
> +     pack-index-expression
>  
>     qualified-id:
>       :: [opt] nested-name-specifier template [opt] unqualified-id
> @@ -6593,7 +6650,9 @@ cp_parser_id_expression (cp_parser *parser,
>       identifier
>       operator-function-id
>       conversion-function-id
> -     ~ class-name
> +     literal-operator-id
> +     ~ type-name
> +     ~ computed-type-specifier
>       template-id
>  
>     If TEMPLATE_KEYWORD_P is TRUE, we have just seen the `template'
> @@ -6900,6 +6959,14 @@ cp_parser_unqualified_id (cp_parser* parser,
>                   "typedef-name %qD used as destructor declarator",
>                   type_decl);
>  
> +     /* "~T...[N]" is a C++26 pack-index-specifier.  */
> +     if (cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS)
> +         && cp_lexer_nth_token_is (parser->lexer, 2, CPP_OPEN_SQUARE))
> +       {
> +         type_decl = cp_parser_pack_index (parser, type_decl);
> +         return build_min_nt_loc (loc, BIT_NOT_EXPR, type_decl);
> +       }
> +
>       return build_min_nt_loc (loc, BIT_NOT_EXPR, TREE_TYPE (type_decl));
>        }
>  
> @@ -6970,9 +7037,10 @@ check_template_keyword_in_nested_name_spec (tree name)
>       class-or-namespace-name :: nested-name-specifier [opt]
>       class-or-namespace-name :: template nested-name-specifier [opt]
>  
> -   nested-name-specifier: [C++0x]
> +   nested-name-specifier: [C++11]
>       type-name ::
>       namespace-name ::
> +     computed-type-specifier ::
>       nested-name-specifier identifier ::
>       nested-name-specifier template [opt] simple-template-id ::
>  
> @@ -7080,6 +7148,10 @@ cp_parser_nested_name_specifier_opt (cp_parser *parser,
>           }
>  
>         if (token->type != CPP_SCOPE
> +           /* See if a pack-index-specifier follows.  */
> +           && !(token->type == CPP_ELLIPSIS
> +                && cp_lexer_peek_nth_token (parser->lexer, 3)->type
> +                   == CPP_OPEN_SQUARE)
>             && !cp_parser_nth_token_starts_template_argument_list_p
>                 (parser, 2))
>           break;
> @@ -7127,6 +7199,12 @@ cp_parser_nested_name_specifier_opt (cp_parser *parser,
>                                         check_dependency_p,
>                                         type_p,
>                                         is_declaration);
> +
> +      /* "T...[constant-expression]" is a C++26 pack-index-specifier.  */
> +      if (cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS)
> +       && cp_lexer_nth_token_is (parser->lexer, 2, CPP_OPEN_SQUARE))
> +      new_scope = cp_parser_pack_index (parser, new_scope);
> +
>        /* Look for the `::' token.  */
>        cp_parser_require (parser, CPP_SCOPE, RT_SCOPE);
>  
> @@ -17938,13 +18016,17 @@ cp_parser_mem_initializer (cp_parser* parser)
>  /* Parse a mem-initializer-id.
>  
>     mem-initializer-id:
> -     :: [opt] nested-name-specifier [opt] class-name
> -     decltype-specifier (C++11)
> +     class-or-decltype
>       identifier
>  
> +  class-or-decltype:
> +    nested-name-specifier [opt] type-name
> +    nested-name-specifier template simple-template-id
> +    computed-type-specifier
> +
>     Returns a TYPE indicating the class to be initialized for the first
> -   production (and the second in C++11).  Returns an IDENTIFIER_NODE
> -   indicating the data member to be initialized for the last production.  */
> +   production.  Returns an IDENTIFIER_NODE indicating the data member to
> +   be initialized for the second production.  */
>  
>  static tree
>  cp_parser_mem_initializer_id (cp_parser* parser)
> @@ -18015,10 +18097,16 @@ cp_parser_mem_initializer_id (cp_parser* parser)
>                              /*class_head_p=*/false,
>                              /*is_declaration=*/true);
>    /* If we found one, we're done.  */
> -  if (cp_parser_parse_definitely (parser))
> -    return id;
> -  /* Otherwise, look for an ordinary identifier.  */
> -  return cp_parser_identifier (parser);
> +  if (!cp_parser_parse_definitely (parser))
> +    /* Otherwise, look for an ordinary identifier.  */
> +    id = cp_parser_identifier (parser);
> +
> +  /* ": T...[N]" is a C++26 pack-index-specifier.  */
> +  if (cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS)
> +      && cp_lexer_nth_token_is (parser->lexer, 2, CPP_OPEN_SQUARE))
> +    id = cp_parser_pack_index (parser, id);
> +
> +  return id;
>  }
>  
>  /* Overloading [gram.over] */
> @@ -20436,11 +20524,11 @@ cp_parser_type_specifier (cp_parser* parser,
>     C++11 Extension:
>  
>     simple-type-specifier:
> -     auto
> -     decltype ( expression )
>       char16_t
>       char32_t
>       __underlying_type ( type-id )
> +     computed-type-specifier
> +     placeholder-type-specifier
>  
>     C++17 extension:
>  
> @@ -20822,6 +20910,13 @@ cp_parser_simple_type_specifier (cp_parser* parser,
>           type = NULL_TREE;
>       }
>  
> +      /* "T...[constant-expression]" is a C++26 pack-index-specifier.  */
> +      if (type
> +       && type != error_mark_node
> +       && cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS)
> +       && cp_lexer_nth_token_is (parser->lexer, 2, CPP_OPEN_SQUARE))
> +     type = cp_parser_pack_index (parser, type);
> +
>        if (!type && flag_concepts && decl_specs)
>       {
>         /* Try for a type-constraint with template arguments.  We check
> @@ -29103,12 +29198,21 @@ cp_parser_base_clause (cp_parser* parser)
>  /* Parse a base-specifier.
>  
>     base-specifier:
> -     attribute-specifier-seq [opt] :: [opt] nested-name-specifier [opt]
> -       class-name
> -     attribute-specifier-seq [opt] virtual access-specifier [opt] :: [opt]
> -       nested-name-specifier [opt] class-name
> -     attribute-specifier-seq [opt] access-specifier virtual [opt] :: [opt]
> -       nested-name-specifier [opt] class-name
> +     attribute-specifier-seq [opt] class-or-decltype
> +     attribute-specifier-seq [opt] virtual access-specifier [opt]
> +       class-or-decltype
> +     attribute-specifier-seq [opt] access-specifier virtual [opt]
> +       class-or-decltype
> +
> +   class-or-decltype:
> +     nested-name-specifier [opt] type-name
> +     nested-name-specifier template simple-template-id
> +     computed-type-specifier
> +
> +   access-specifier:
> +     private
> +     protected
> +     public
>  
>     Returns a TREE_LIST.  The TREE_PURPOSE will be one of
>     ACCESS_{DEFAULT,PUBLIC,PROTECTED,PRIVATE}_[VIRTUAL]_NODE to
> @@ -29237,6 +29341,10 @@ cp_parser_base_specifier (cp_parser* parser)
>                                  /*class_head_p=*/false,
>                                  /*is_declaration=*/true);
>        type = TREE_TYPE (type);
> +      /* ": T...[constant-expression]" is a C++26 pack-index-specifier.  */
> +      if (cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS)
> +       && cp_lexer_nth_token_is (parser->lexer, 2, CPP_OPEN_SQUARE))
> +     type = cp_parser_pack_index (parser, type);
>      }
>  
>    if (type == error_mark_node)
> diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
> index b590c32345f..e7f3ce38348 100644
> --- a/gcc/cp/pt.cc
> +++ b/gcc/cp/pt.cc
> @@ -1786,7 +1786,8 @@ iterative_hash_template_arg (tree arg, hashval_t val)
>      case TYPE_PACK_EXPANSION:
>      case EXPR_PACK_EXPANSION:
>        val = iterative_hash_template_arg (PACK_EXPANSION_PATTERN (arg), val);
> -      return iterative_hash_template_arg (PACK_EXPANSION_EXTRA_ARGS (arg), 
> val);
> +      val = iterative_hash_template_arg (PACK_EXPANSION_EXTRA_ARGS (arg), 
> val);
> +      return iterative_hash_template_arg (PACK_EXPANSION_INDEX (arg), val);
>  
>      case TYPE_ARGUMENT_PACK:
>      case NONTYPE_ARGUMENT_PACK:
> @@ -3952,7 +3953,11 @@ find_parameter_packs_r (tree *tp, int *walk_subtrees, 
> void* data)
>        break;
>  
>      case VAR_DECL:
> -      if (DECL_PACK_P (t))
> +      /* We can have
> +        T...[0] a;
> +        (T...[0])(a); // #1
> +      where the 'a' in #1 is not a bare parameter pack.  */
> +      if (DECL_PACK_P (t) && !PACK_EXPANSION_INDEX (TREE_TYPE (t)))
>          {
>            /* We don't want to walk into the type of a variadic capture proxy,
>               because we don't want to see the type parameter pack.  */
> @@ -4027,6 +4032,10 @@ find_parameter_packs_r (tree *tp, int *walk_subtrees, 
> void* data)
>  
>      case TYPE_PACK_EXPANSION:
>      case EXPR_PACK_EXPANSION:
> +      /* We can have an expansion of an expansion, such as "Ts...[Is]...",
> +      so do look into the index.  */
> +      cp_walk_tree (&PACK_EXPANSION_INDEX (t), &find_parameter_packs_r, ppd,
> +                 ppd->visited);
>        *walk_subtrees = 0;
>        return NULL_TREE;
>  
> @@ -12524,11 +12533,19 @@ instantiate_class_template (tree type)
>  
>            if (PACK_EXPANSION_P (BINFO_TYPE (pbase_binfo)))
>              {
> -              expanded_bases = 
> +           expanded_bases =
>               tsubst_pack_expansion (BINFO_TYPE (pbase_binfo),
>                                      args, tf_error, NULL_TREE);
>                if (expanded_bases == error_mark_node)
>                  continue;
> +           /* If there was a pack-index-specifier, we won't get
> +              a TREE_VEC but the rest of the code assumes so.  */
> +           if (PACK_EXPANSION_INDEX (BINFO_TYPE (pbase_binfo)))
> +             {
> +               tree vec = make_tree_vec (1);
> +               TREE_VEC_ELT (vec, 0) = expanded_bases;
> +               expanded_bases = vec;
> +             }
>  
>                len = TREE_VEC_LENGTH (expanded_bases);
>              }
> @@ -13669,6 +13686,8 @@ tsubst_pack_expansion (tree t, tree args, 
> tsubst_flags_t complain,
>  
>    levels = TMPL_ARGS_DEPTH (args);
>  
> +  tree index = tsubst_expr (PACK_EXPANSION_INDEX (t), args, complain, 
> in_decl);
> +
>    /* Determine the argument packs that will instantiate the parameter
>       packs used in the expansion expression. While we're at it,
>       compute the number of arguments to be expanded and make sure it
> @@ -13814,6 +13833,10 @@ tsubst_pack_expansion (tree t, tree args, 
> tsubst_flags_t complain,
>      {
>        tree args = ARGUMENT_PACK_ARGS (TREE_VALUE (packs));
>  
> +      /* C++26 Pack Indexing.  */
> +      if (index)
> +     return pack_index_element (index, args, complain);

I'd expect every pack index operator to hit this code path since its
pattern should always be a bare pack...

> +
>        /* If the argument pack is a single pack expansion, pull it out.  */
>        if (TREE_VEC_LENGTH (args) == 1
>         && pack_expansion_args_count (args))
> @@ -13946,6 +13969,10 @@ tsubst_pack_expansion (tree t, tree args, 
> tsubst_flags_t complain,
>        && PACK_EXPANSION_P (TREE_VEC_ELT (result, 0)))
>      return TREE_VEC_ELT (result, 0);
>  
> +  /* C++26 Pack Indexing.  */
> +  if (index)
> +    return pack_index_element (index, result, complain);

... so this code path should be necessary?

> +
>    return result;
>  }
>  
> @@ -16897,17 +16924,23 @@ tsubst (tree t, tree args, tsubst_flags_t complain, 
> tree in_decl)
>           ctx = tsubst_pack_expansion (ctx, args,
>                                        complain | tf_qualifying_scope,
>                                        in_decl);
> -         if (ctx == error_mark_node
> -             || TREE_VEC_LENGTH (ctx) > 1)
> +         if (ctx == error_mark_node)
>             return error_mark_node;
> -         if (TREE_VEC_LENGTH (ctx) == 0)
> +         /* If there was a pack-index-specifier, we won't get a TREE_VEC,
> +            just the single element.  */
> +         if (TREE_CODE (ctx) == TREE_VEC)
>             {
> -             if (complain & tf_error)
> -               error ("%qD is instantiated for an empty pack",
> -                      TYPENAME_TYPE_FULLNAME (t));
> -             return error_mark_node;
> +             if (TREE_VEC_LENGTH (ctx) > 1)
> +               return error_mark_node;
> +             if (TREE_VEC_LENGTH (ctx) == 0)
> +               {
> +                 if (complain & tf_error)
> +                   error ("%qD is instantiated for an empty pack",
> +                          TYPENAME_TYPE_FULLNAME (t));
> +                 return error_mark_node;
> +               }
> +             ctx = TREE_VEC_ELT (ctx, 0);
>             }
> -         ctx = TREE_VEC_ELT (ctx, 0);
>         }
>       else
>         ctx = tsubst_entering_scope (ctx, args,
> @@ -17041,13 +17074,20 @@ tsubst (tree t, tree args, tsubst_flags_t complain, 
> tree in_decl)
>       else
>         {
>           bool id = DECLTYPE_TYPE_ID_EXPR_OR_MEMBER_ACCESS_P (t);
> -         if (id && TREE_CODE (DECLTYPE_TYPE_EXPR (t)) == BIT_NOT_EXPR
> -             && EXPR_P (type))
> +         tree op = DECLTYPE_TYPE_EXPR (t);
> +         if (id && TREE_CODE (op) == BIT_NOT_EXPR && EXPR_P (type))
>             /* In a template ~id could be either a complement expression
>                or an unqualified-id naming a destructor; if instantiating
>                it produces an expression, it's not an id-expression or
>                member access.  */
>             id = false;
> +         /* With pack indexing, we don't know what it's going to expand to
> +            until instantiation.  The intent is that a pack indexing
> +            expression behaves exactly as the underlying expression
> +            would.  */
> +         else if (PACK_EXPANSION_P (op))
> +           id = (!PACK_EXPANSION_PARENTHESIZED_P (op)
> +                 && unparenthesized_id_or_class_member_access_p (type));
>           type = finish_decltype_type (type, id, complain);
>         }
>       return cp_build_qualified_type (type,
> @@ -17074,6 +17114,11 @@ tsubst (tree t, tree args, tsubst_flags_t complain, 
> tree in_decl)
>      case NONTYPE_ARGUMENT_PACK:
>        return tsubst_argument_pack (t, args, complain, in_decl);
>  
> +    case TYPE_PACK_EXPANSION:
> +      if (PACK_EXPANSION_INDEX (t))
> +     return tsubst_pack_expansion (t, args, complain, in_decl);
> +      gcc_fallthrough ();
> +
>      case VOID_CST:
>      case INTEGER_CST:
>      case REAL_CST:
> @@ -19596,6 +19641,8 @@ tsubst_stmt (tree t, tree args, tsubst_flags_t 
> complain, tree in_decl)
>        }
>  
>      case EXPR_PACK_EXPANSION:
> +      if (PACK_EXPANSION_INDEX (t))
> +     RETURN (tsubst_pack_expansion (t, args, complain, in_decl));
>        error ("invalid use of pack expansion expression");
>        RETURN (error_mark_node);
>  
> @@ -21776,6 +21823,8 @@ tsubst_expr (tree t, tree args, tsubst_flags_t 
> complain, tree in_decl)
>        }
>  
>      case EXPR_PACK_EXPANSION:
> +      if (PACK_EXPANSION_INDEX (t))
> +     RETURN (tsubst_pack_expansion (t, args, complain, in_decl));
>        error ("invalid use of pack expansion expression");
>        RETURN (error_mark_node);
>  
> @@ -27685,6 +27734,14 @@ tsubst_initializer_list (tree t, tree argvec)
>                                                   NULL_TREE);
>            if (expanded_bases == error_mark_node)
>              continue;
> +       /* If there was a pack-index-specifier, we won't get
> +          a TREE_VEC but the rest of the code assumes so.  */
> +       if (PACK_EXPANSION_INDEX (TREE_PURPOSE (t)))
> +         {
> +           tree vec = make_tree_vec (1);
> +           TREE_VEC_ELT (vec, 0) = expanded_bases;
> +           expanded_bases = vec;
> +         }
>  
>            /* We'll be building separate TREE_LISTs of arguments for
>               each base.  */
> @@ -28115,7 +28172,7 @@ dependent_type_p_r (tree type)
>      }
>  
>    /* All TYPE_PACK_EXPANSIONs are dependent, because parameter packs must
> -     be template parameters.  */
> +     be template parameters.  This includes pack-index-specifiers.  */
>    if (TREE_CODE (type) == TYPE_PACK_EXPANSION)
>      return true;
>  
> @@ -28739,7 +28796,8 @@ type_dependent_expression_p (tree expression)
>        && uses_outer_template_parms_in_constraints (expression))
>      return true;
>  
> -  /* Always dependent, on the number of arguments if nothing else.  */
> +  /* Always dependent, on the number of arguments if nothing else.  This
> +     includes pack-index-expressions.  */
>    if (TREE_CODE (expression) == EXPR_PACK_EXPANSION)
>      return true;
>  
> @@ -31166,7 +31224,7 @@ do_class_deduction (tree ptype, tree tmpl, tree init, 
> tree outer_targs,
>  
>  /* Return true if INIT is an unparenthesized id-expression or an
>     unparenthesized class member access.  Used for the argument of
> -   decltype(auto).  */
> +   decltype(auto), or for C++26 pack indexing.  */
>  
>  bool
>  unparenthesized_id_or_class_member_access_p (tree init)
> diff --git a/gcc/cp/ptree.cc b/gcc/cp/ptree.cc
> index 15e46752d01..82d08a31b22 100644
> --- a/gcc/cp/ptree.cc
> +++ b/gcc/cp/ptree.cc
> @@ -190,6 +190,7 @@ cxx_print_type (FILE *file, tree node, int indent)
>      case TYPE_PACK_EXPANSION:
>        print_node (file, "pattern", PACK_EXPANSION_PATTERN (node), indent + 
> 4);
>        print_node (file, "args", PACK_EXPANSION_EXTRA_ARGS (node), indent + 
> 4);
> +      print_node (file, "index", PACK_EXPANSION_INDEX (node), indent + 4);
>        return;
>  
>      default:
> diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
> index dabfb1ff53f..7ccd678252c 100644
> --- a/gcc/cp/semantics.cc
> +++ b/gcc/cp/semantics.cc
> @@ -2452,6 +2452,8 @@ finish_parenthesized_expr (cp_expr expr)
>    tree stripped_expr = tree_strip_any_location_wrapper (expr);
>    if (TREE_CODE (stripped_expr) == STRING_CST)
>      PAREN_STRING_LITERAL_P (stripped_expr) = 1;
> +  else if (PACK_EXPANSION_P (stripped_expr))
> +    PACK_EXPANSION_PARENTHESIZED_P (stripped_expr) = true;
>  
>    expr = cp_expr (force_paren_expr (expr), expr.get_location ());
>  
> @@ -4181,7 +4183,9 @@ finish_base_specifier (tree base, tree access, bool 
> virtual_p)
>        error ("invalid base-class specification");
>        result = NULL_TREE;
>      }
> -  else if (! MAYBE_CLASS_TYPE_P (base))
> +  else if (! MAYBE_CLASS_TYPE_P (base)
> +        && ! (PACK_EXPANSION_P (base)
> +              && PACK_EXPANSION_INDEX (base)))
>      {
>        error ("%qT is not a class type", base);
>        result = NULL_TREE;
> @@ -4837,34 +4841,66 @@ finish_underlying_type (tree type)
>    return underlying_type;
>  }
>  
> -/* Implement the __type_pack_element keyword: Return the type
> -   at index IDX within TYPES.  */
> +/* Return the type at index IDX within TYPES.  */
>  
>  static tree
> -finish_type_pack_element (tree idx, tree types, tsubst_flags_t complain)
> +get_vec_elt_checking (tree idx, tree types, bool pack_index_p,
> +                   tsubst_flags_t complain)
>  {
>    idx = maybe_constant_value (idx);
>    if (TREE_CODE (idx) != INTEGER_CST || !INTEGRAL_TYPE_P (TREE_TYPE (idx)))
>      {
>        if (complain & tf_error)
> -     error ("%<__type_pack_element%> index is not an integral constant");
> +     {
> +       if (pack_index_p)
> +         error ("pack index is not an integral constant");
> +       else
> +         error ("%<__type_pack_element%> index is not an integral constant");
> +     }
>        return error_mark_node;
>      }
>    if (tree_int_cst_sgn (idx) < 0)
>      {
>        if (complain & tf_error)
> -     error ("%<__type_pack_element%> index is negative");
> +     {
> +       if (pack_index_p)
> +         error ("pack index is negative");
> +       else
> +         error ("%<__type_pack_element%> index is negative");
> +     }
>        return error_mark_node;
>      }
>    if (wi::to_widest (idx) >= TREE_VEC_LENGTH (types))
>      {
>        if (complain & tf_error)
> -     error ("%<__type_pack_element%> index is out of range");
> +     {
> +       if (pack_index_p)
> +         error ("pack index is out of range");
> +       else
> +         error ("%<__type_pack_element%> index is out of range");
> +     }
>        return error_mark_node;
>      }
>    return TREE_VEC_ELT (types, tree_to_shwi (idx));
>  }
>  
> +/* Implement the __type_pack_element keyword: Return the type
> +   at index IDX within TYPES.  */
> +
> +static tree
> +finish_type_pack_element (tree idx, tree types, tsubst_flags_t complain)
> +{
> +  return get_vec_elt_checking (idx, types, /*pack_index_p=*/false, complain);
> +}
> +
> +/* In a pack-index T...[N], return the element at index IDX within TYPES.  */
> +
> +tree
> +pack_index_element (tree idx, tree types, tsubst_flags_t complain)
> +{
> +  return get_vec_elt_checking (idx, types, /*pack_index_p=*/true, complain);
> +}
> +
>  /* Implement the __direct_bases keyword: Return the direct base classes
>     of type.  */
>  
> diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc
> index c80ee068958..8637ab06d77 100644
> --- a/gcc/cp/tree.cc
> +++ b/gcc/cp/tree.cc
> @@ -1441,7 +1441,10 @@ cp_build_qualified_type (tree type, int type_quals,
>        tree t = PACK_EXPANSION_PATTERN (type);
>  
>        t = cp_build_qualified_type (t, type_quals, complain);
> -      return make_pack_expansion (t, complain);
> +      /* Regenerate the pack expansion with a qualified type.  */
> +      t = make_pack_expansion (t, complain);
> +      PACK_EXPANSION_INDEX (t) = PACK_EXPANSION_INDEX (type);
> +      return t;
>      }
>  
>    /* A reference or method type shall not be cv-qualified.
> @@ -4242,6 +4245,9 @@ cp_tree_equal (tree t1, tree t2)
>        if (!comp_template_args (PACK_EXPANSION_EXTRA_ARGS (t1),
>                              PACK_EXPANSION_EXTRA_ARGS (t2)))
>       return false;
> +      if (!cp_tree_equal (PACK_EXPANSION_INDEX (t1),
> +                       PACK_EXPANSION_INDEX (t2)))
> +     return false;
>        return true;
>  
>      case COMPONENT_REF:
> @@ -5575,12 +5581,16 @@ cp_walk_subtrees (tree *tp, int *walk_subtrees_p, 
> walk_tree_fn func,
>      case TYPE_PACK_EXPANSION:
>        WALK_SUBTREE (TREE_TYPE (t));
>        WALK_SUBTREE (PACK_EXPANSION_EXTRA_ARGS (t));
> +      if (PACK_EXPANSION_INDEX (t))
> +     WALK_SUBTREE (PACK_EXPANSION_INDEX (t));
>        *walk_subtrees_p = 0;
>        break;
> -      
> +
>      case EXPR_PACK_EXPANSION:
>        WALK_SUBTREE (TREE_OPERAND (t, 0));
>        WALK_SUBTREE (PACK_EXPANSION_EXTRA_ARGS (t));
> +      if (PACK_EXPANSION_INDEX (t))
> +     WALK_SUBTREE (PACK_EXPANSION_INDEX (t));
>        *walk_subtrees_p = 0;
>        break;
>  
> diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc
> index 71d879abef1..4f0d1bc8af2 100644
> --- a/gcc/cp/typeck.cc
> +++ b/gcc/cp/typeck.cc
> @@ -1620,7 +1620,9 @@ structural_comptypes (tree t1, tree t2, int strict)
>        return (same_type_p (PACK_EXPANSION_PATTERN (t1),
>                          PACK_EXPANSION_PATTERN (t2))
>             && comp_template_args (PACK_EXPANSION_EXTRA_ARGS (t1),
> -                                  PACK_EXPANSION_EXTRA_ARGS (t2)));
> +                                  PACK_EXPANSION_EXTRA_ARGS (t2))
> +           && cp_tree_equal (PACK_EXPANSION_INDEX (t1),
> +                             PACK_EXPANSION_INDEX (t2)));
>  
>      case DECLTYPE_TYPE:
>        if (DECLTYPE_TYPE_ID_EXPR_OR_MEMBER_ACCESS_P (t1)
> diff --git a/gcc/testsuite/g++.dg/cpp26/pack-indexing1.C 
> b/gcc/testsuite/g++.dg/cpp26/pack-indexing1.C
> new file mode 100644
> index 00000000000..9d72c6582af
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp26/pack-indexing1.C
> @@ -0,0 +1,102 @@
> +// P2662R3 - Pack Indexing
> +// PR c++/113798
> +// { dg-do compile { target c++17 } }
> +// { dg-options "" }
> +
> +template<class, class> struct same_type;
> +template<class T> struct same_type<T, T> {};
> +
> +template<int I, typename... Ts>
> +using Type = Ts...[I]; // { dg-warning "pack indexing only available with" 
> "" { target c++23_down } }
> +
> +template<int I, auto... Ts>
> +constexpr auto Var = Ts...[I]; // { dg-warning "pack indexing only available 
> with" "" { target c++23_down } }
> +
> +template <int I, auto...Ts>
> +decltype(Ts...[I])  // { dg-warning "pack indexing only available with" "" { 
> target c++23_down } }
> +foo ()
> +{
> +  return Ts...[I]; // { dg-warning "pack indexing only available with" "" { 
> target c++23_down } }
> +}
> +
> +template<typename... Ts>
> +struct S {
> +  Ts...[0] a; // { dg-warning "pack indexing only available with" "" { 
> target c++23_down } }
> +#if __cpp_concepts >= 201907L
> +  void foo (auto... Vs) {
> +    decltype(Vs...[1]) d1 = Vs...[1]; // { dg-warning "pack indexing only 
> available with" "" { target { c++20 && c++23_down } } }
> +  }
> +#endif
> +};
> +
> +int
> +g ()
> +{
> +  using U = Type<1, char, int, float>;
> +  using U = int;
> +
> +  constexpr auto V = Var<2, 0, 1, 42>;
> +  static_assert (V == 42);
> +
> +  U r = foo<2, 0, 1, 42>();
> +
> +  return r;
> +}
> +
> +void
> +fn1 ()
> +{
> +  int i = 0;
> +  [&i](auto... pack) {
> +    // type is int
> +    decltype(pack...[0]) x5 = 42; // { dg-warning "pack indexing only 
> available with" "" { target c++23_down } }
> +    // type is int&
> +    [[maybe_unused]] decltype((pack...[0])) x6 = i; // { dg-warning "pack 
> indexing only available with" "" { target c++23_down } }
> +  }(0);
> +}
> +
> +#if __cpp_concepts >= 201907L
> +int
> +bar (auto... pack)
> +{
> +  (void) pack...[0]; // { dg-warning "pack indexing only available with" "" 
> { target { c++20 && c++23_down } } }
> +  int x = pack...[0]; // { dg-warning "pack indexing only available with" "" 
> { target { c++20 && c++23_down } } }
> +  return x;
> +}
> +#endif
> +
> +template<auto...pack>
> +void
> +fn2 ()
> +{
> +  [[maybe_unused]] decltype(pack...[0]) x1; // { dg-warning "pack indexing 
> only available with" "" { target c++23_down } }
> +  [[maybe_unused]] decltype((pack...[0])) x2; // { dg-warning "pack indexing 
> only available with" "" { target c++23_down } }
> +  same_type<decltype(x1), int>();
> +  same_type<decltype(x2), int>();
> +}
> +
> +template<typename... T>
> +void
> +fn3 (int p)
> +{
> +  T...[0] a = p; // { dg-warning "pack indexing only available with" "" { 
> target c++23_down } }
> +  (T...[0])(a);  // { dg-warning "pack indexing only available with" "" { 
> target c++23_down } }
> +}
> +
> +template<int... Is>
> +void fn4 ()
> +{
> +  same_type<decltype(Is...[0]), int>();      // { dg-warning "pack indexing 
> only available with" "" { target c++23_down } }
> +  same_type<decltype((Is...[0])), int>(); // { dg-warning "pack indexing 
> only available with" "" { target c++23_down } }
> +}
> +
> +void
> +g3 ()
> +{
> +  fn2<0>();
> +#if __cpp_concepts >= 201907L
> +  bar (0);
> +#endif
> +  S<int> s;
> +  fn4<0, 1, 2>();
> +}
> diff --git a/gcc/testsuite/g++.dg/cpp26/pack-indexing2.C 
> b/gcc/testsuite/g++.dg/cpp26/pack-indexing2.C
> new file mode 100644
> index 00000000000..c3c67d6ea34
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp26/pack-indexing2.C
> @@ -0,0 +1,111 @@
> +// P2662R3 - Pack Indexing
> +// PR c++/113798
> +// { dg-do compile { target c++26 } }
> +// Test invalid cases.
> +
> +template<int I, typename... Ts>
> +using Type = Typo...[I]; // { dg-error "does not name a type" }
> +
> +template<int I, auto... Ts>
> +constexpr auto Var = Typo...[I]; // { dg-error "no parameter packs" }
> +
> +template<typename... Ts>
> +void foo(Ts...[]);  // { dg-error "pack index missing" }
> +
> +template <typename... Ts>
> +void f(Ts...[1]);
> +
> +template<int... N>
> +int f2 (X... [N]); // { dg-error "contains no parameter packs" }
> +
> +struct X;
> +
> +template<typename T>
> +struct TX;
> +
> +template <typename T, auto V, template<typename> typename Tp>
> +void
> +bad (int i)
> +{
> +  i...[0];  // { dg-error "no parameter packs" }
> +  V...[0];  // { dg-error "no parameter packs" }
> +  X...[0] x;  // { dg-error "no parameter packs" }
> +  T...[0] t;  // { dg-error "no parameter packs" }
> +  Tp...[0] tp;  // { dg-error "expected" }
> +
> +  X...[0] xarr[1];  // { dg-error "no parameter packs" }
> +  T...[0] tarr[1];  // { dg-error "no parameter packs" }
> +  Tp...[0] tparr[1];  // { dg-error "expected" }
> +}
> +
> +template<int N>
> +int
> +getT (auto... Ts)
> +{
> +  return Ts...[N]; // { dg-error "pack index is out of range" }
> +}
> +
> +template<int N>
> +int
> +getT2 (auto... Ts)
> +{
> +  return Ts...[N]; // { dg-error "pack index is negative" }
> +}
> +
> +template<auto N, typename... Ts>
> +void
> +badtype ()
> +{
> +  Ts...[N] t; // { dg-error "pack index is out of range" }
> +}
> +
> +template<auto N, typename... Ts>
> +void
> +badtype2 ()
> +{
> +  Ts...[N] t; // { dg-error "pack index is negative" }
> +}
> +
> +int nonconst () { return 42; }
> +
> +template<typename... Ts>
> +void
> +badindex ()
> +{
> +  Ts...[nonconst ()] t; // { dg-error "pack index is not an integral 
> constant" }
> +  // { dg-error "non-.constexpr. function" "" { target *-*-* } .-1 }
> +}
> +
> +template<typename... Ts>
> +struct broken {
> +  Ts...1;  // { dg-error "expected" }
> +  Ts...[;  // { dg-error "invalid" }
> +  Ts...[1;  // { dg-error "invalid" }
> +  Ts...[];  // { dg-error "pack index missing" }
> +
> +  void foo (auto...Vs) {
> +    decltype(Vs...[1]) d1 = Vs...[]; // { dg-error "pack index missing" }
> +    decltype(Vs...[1]) d2 = Vs...[; // { dg-error "expected" }
> +  }
> +};
> +
> +int main()
> +{
> +   // void f<int, double>(int [1], double [1])
> +  f<int, double>(nullptr, nullptr); // { dg-error "cannot convert" }
> +  bad<int, 0, TX>(42);
> +
> +  getT<0>(); // { dg-message "required from here" }
> +  getT<1>();  // { dg-message "required from here" }
> +  getT2<-1>();  // { dg-message "required from here" }
> +
> +  badtype<0>(); // { dg-message "required from here" }
> +  badtype<1, int>(); // { dg-message "required from here" }
> +  badtype2<-1>(); // { dg-message "required from here" }
> +  badtype2<-1, int>(); // { dg-message "required from here" }
> +
> +  badindex<int, int, int>();
> +
> +  bool b = nothere...[0]; // { dg-error "no parameter packs" }
> +  using E = nothere...[0]; // { dg-error "does not name a type" }
> +}
> diff --git a/gcc/testsuite/g++.dg/cpp26/pack-indexing3.C 
> b/gcc/testsuite/g++.dg/cpp26/pack-indexing3.C
> new file mode 100644
> index 00000000000..8c10b307f3a
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp26/pack-indexing3.C
> @@ -0,0 +1,41 @@
> +// P2662R3 - Pack Indexing
> +// PR c++/113798
> +// { dg-do compile { target c++26 } }
> +// From LLVM's cxx2c-pack-indexing.cpp.
> +
> +template<typename...>
> +struct X { };
> +
> +template<typename... T>
> +requires requires(T...[0]) { {T...[0](0)}; }
> +struct S : T...[1] {
> +  [[maybe_unused]] T...[1] base = {};
> +  using foo = T...[1];
> +  S() : T...[1]() { }
> +  X<T...[0]> x;
> +  const T...[0] f(T...[0]&& parm) noexcept((T...[0])0) {
> +    T...[0] (*test)(const volatile T...[0]**);
> +    thread_local T...[0] d;
> +    [[maybe_unused]] T...[0] a = parm;
> +    auto ptr = new T...[0](0);
> +    (*ptr).~T...[0]();
> +    return T...[0](0);
> +    typename T...[1]::foo b = 0;
> +    T...[1]::i = 0;
> +    return (T...[0])(a);
> +    new T...[0];
> +    [[maybe_unused]] auto l = []<T...[0]>(T...[0][1]) -> T...[0]{ return {}; 
> };
> +    [[maybe_unused]] auto _ = l.template operator()<T...[0]{}>({0});
> +  }
> +  operator T...[0]() const { }
> +};
> +
> +struct base {
> +    using foo = int;
> +    static inline int i = 42;
> +};
> +
> +int main()
> +{
> +  S<int, base>().f(0);
> +}
> diff --git a/gcc/testsuite/g++.dg/cpp26/pack-indexing4.C 
> b/gcc/testsuite/g++.dg/cpp26/pack-indexing4.C
> new file mode 100644
> index 00000000000..8decf3064bc
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp26/pack-indexing4.C
> @@ -0,0 +1,65 @@
> +// P2662R3 - Pack Indexing
> +// PR c++/113798
> +// { dg-do compile { target c++26 } }
> +// From LLVM's cxx2c-pack-indexing.cpp.
> +
> +template <class, class>
> +constexpr bool is_same = false;
> +template <class T>
> +constexpr bool is_same<T, T> = true;
> +
> +template <typename T>
> +constexpr bool
> +f (auto&&... p)
> +{
> +  return is_same<T, decltype(p...[0])>;
> +}
> +
> +void
> +g ()
> +{
> +    int a = 0;
> +    const int b = 0;
> +    static_assert(f<int&&>(0));
> +    static_assert(f<int&>(a));
> +    static_assert(f<const int&>(b));
> +}
> +
> +template<auto... p>
> +struct A {
> +  enum E {
> +    x = p...[0]
> +  };
> +};
> +static_assert(A<42>::x == 42);
> +
> +struct S { };
> +template<auto... p>
> +constexpr auto constant_initializer = p...[0];
> +constexpr auto InitOk = constant_initializer<S{}>;
> +
> +consteval int evaluate(auto... p) {
> +    return p...[0];
> +}
> +constexpr int x = evaluate(42, S{});
> +static_assert(x == 42);
> +
> +template <auto... Is>
> +struct IL{};
> +
> +template <typename... Ts>
> +struct TL{};
> +
> +template <typename Tl, typename Il>
> +struct SpliceImpl;
> +
> +template <typename... Ts, auto... Is>
> +struct SpliceImpl<TL<Ts...>, IL<Is...>>
> +{
> +    using type = TL<Ts...[Is]...>;
> +};
> +
> +template <typename Tl, typename Il>
> +using Splice = typename SpliceImpl<Tl, Il>::type;
> +using type = Splice<TL<char, short, long, double>, IL<1, 2>>;
> +static_assert(is_same<type, TL<short, long>>);
> diff --git a/gcc/testsuite/g++.dg/cpp26/pack-indexing5.C 
> b/gcc/testsuite/g++.dg/cpp26/pack-indexing5.C
> new file mode 100644
> index 00000000000..901956e2dae
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp26/pack-indexing5.C
> @@ -0,0 +1,41 @@
> +// P2662R3 - Pack Indexing
> +// PR c++/113798
> +// { dg-do compile { target c++26 } }
> +
> +template<class, class> struct same_type;
> +template<class T> struct same_type<T, T> {};
> +
> +void
> +fn1 (auto... Ts)
> +{
> +  same_type<decltype(Ts...[0]), int>();
> +  same_type<decltype((Ts...[0])), int&>();
> +  same_type<decltype(Ts...[1]), unsigned int>();
> +  same_type<decltype((Ts...[1])), unsigned int&>();
> +}
> +
> +template<auto... Is>
> +void
> +fn2 ()
> +{
> +  same_type<decltype(Is...[0]), int>();
> +  same_type<decltype((Is...[0])), int>();
> +  same_type<decltype(Is...[1]), unsigned int>();
> +  same_type<decltype((Is...[1])), unsigned int>();
> +  same_type<decltype(Is...[2]), double>();
> +  same_type<decltype((Is...[2])), double>();
> +  same_type<decltype(Is...[3]), float>();
> +  same_type<decltype((Is...[3])), float>();
> +  same_type<decltype(Is...[4]), unsigned char>();
> +  same_type<decltype((Is...[4])), unsigned char>();
> +}
> +
> +static constexpr unsigned char c = 'A';
> +
> +void
> +g ()
> +{
> +  int i = 42;
> +  fn1 (i, 42u);
> +  fn2<0, 1u, 2.0, 3.f, c>();
> +}
> 
> base-commit: 5fd1c0c1b6968d55e3f997d67a4c149edf20c012
> -- 
> 2.46.2
> 
> 

Reply via email to