On 11/6/24 3:33 PM, Marek Polacek wrote:
On Mon, Nov 04, 2024 at 11:10:05PM -0500, Jason Merrill wrote:
On 10/30/24 4:59 PM, Marek Polacek wrote:
On Wed, Oct 30, 2024 at 09:01:36AM -0400, Patrick Palka wrote:
On Tue, 29 Oct 2024, Marek Polacek wrote:
+static tree
+cp_parser_pack_index (cp_parser *parser, tree pack)
+{
+  if (cxx_dialect < cxx26)
+    pedwarn (cp_lexer_peek_token (parser->lexer)->location,
+            OPT_Wc__26_extensions, "pack indexing only available with "
+            "%<-std=c++2c%> or %<-std=gnu++2c%>");
+  /* Consume the '...' token.  */
+  cp_lexer_consume_token (parser->lexer);
+  /* Consume the '['.  */
+  cp_lexer_consume_token (parser->lexer);
+
+  if (cp_lexer_next_token_is (parser->lexer, CPP_CLOSE_SQUARE))
+    {
+      error_at (cp_lexer_peek_token (parser->lexer)->location,
+               "pack index missing");

Maybe cp_parser_error?

Unsure.  This:

   template<typename... Ts>
   void foo(Ts...[]);

then generates:

   error: variable or field 'foo' declared void
   error: expected primary-expression before '...' token
   error: pack index missing before ']' token

which doesn't seem better.

I guess the question is whether we need to deal with the vexing parse. But in this case it'd be ill-formed regardless, so what you have is fine.

@@ -6368,6 +6416,12 @@ cp_parser_primary_expression (cp_parser *parser,
          = make_location (caret_loc, start_loc, finish_loc);
        decl.set_location (combined_loc);
+
+       /* "T...[constant-expression]" is a C++26 pack-index-expression.  */
+       if (cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS)
+           && cp_lexer_nth_token_is (parser->lexer, 2, CPP_OPEN_SQUARE))
+         decl = cp_parser_pack_index (parser, decl);

Shouldn't this be in cp_parser_id_expression?

It should, but I need to wait until after finish_id_expression, so that
DECL isn't just an identifier node.

Ah, makes sense.

+     ~ computed-type-specifier

Hmm, seems we never implemented ~decltype.

Looks like CWG 1753: <https://gcc.gnu.org/PR117450>.

Thanks.

@@ -4031,6 +4036,15 @@ find_parameter_packs_r (tree *tp, int *walk_subtrees, 
void* data)
         *walk_subtrees = 0;
         return NULL_TREE;
+    case PACK_INDEX_TYPE:
+    case PACK_INDEX_EXPR:
+      /* We can have an expansion of an expansion, such as "Ts...[Is]...",
+        so do look into the index.  */
+      cp_walk_tree (&PACK_INDEX_INDEX (t), &find_parameter_packs_r, ppd,
+                   ppd->visited);
+      *walk_subtrees = 0;
+      return NULL_TREE;

Do we need to handle these specifically here?  I'd think the handling in
cp_walk_subtrees would be enough.

I think I do, otherwise the Ts...[Is]... test doesn't work.
It is used when calling check_for_bare_parameter_packs.

Makes sense.

I'm not seeing a test for https://eel.is/c++draft/diff#cpp23.dcl.dcl-2 or
the code to handle this case differently in C++23 vs 26.
Ah, right. I've added the test (pack-indexing11.C) but we don't
compile it C++23 as we should due to:

pack-indexing11.C:7:13: error: expected ',' or '...' before '[' token
     7 | void f(T... [1]);
       |             ^

which seems like a bug.  Opened <https://gcc.gnu.org/PR117472>.

Is fixing that a requirement for this patch?

No. Really, given that we're reusing this grammar, it's probably fine to never fix it.

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.

+  MARK_TS_TYPE_NON_COMMON (PACK_INDEX_TYPE);

I wonder about trying to use the tree_common symtab member for the type index so we don't need non_common, but that's not necessary.

+       if (cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS)
+           && cp_lexer_nth_token_is (parser->lexer, 2, CPP_OPEN_SQUARE))

This happens a lot in the parser changes, how about factoring it into cp_parser_next_tokens_are_pack_index?

Or change cp_parser_pack_index to cp_parser_maybe_pack_index that does this check, then returns the argument if we aren't looking at a pack index?

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

Let's move the error handling into make_pack_index.

@@ -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.  */

Let's add "...but not the pack."

@@ -28119,8 +28194,9 @@ dependent_type_p_r (tree type)
      }
/* All TYPE_PACK_EXPANSIONs are dependent, because parameter packs must
-     be template parameters.  */
-  if (TREE_CODE (type) == TYPE_PACK_EXPANSION)
+     be template parameters.  This includes pack-index-specifiers.  */
+  if (TREE_CODE (type) == TYPE_PACK_EXPANSION
+      || TREE_CODE (type) == PACK_INDEX_TYPE)
      return true;
if (TREE_CODE (type) == DEPENDENT_OPERATOR_TYPE)
@@ -28743,8 +28819,10 @@ type_dependent_expression_p (tree expression)
        && uses_outer_template_parms_in_constraints (expression))
      return true;
- /* Always dependent, on the number of arguments if nothing else. */
-  if (TREE_CODE (expression) == EXPR_PACK_EXPANSION)
+  /* Always dependent, on the number of arguments if nothing else.  This
+     includes pack-index-expressions.  */
+  if (TREE_CODE (expression) == EXPR_PACK_EXPANSION
+      || TREE_CODE (expression) == PACK_INDEX_EXPR)
      return true;

Actually I don't think this includes pack-index-expressions; for them, you always get one result.

https://eel.is/c++draft/temp#dep.expr-8 says it's type-dependent if the pack is type-dependent.

Jason

Reply via email to