On 11/24/21 08:37, Jakub Jelinek wrote:
On Tue, Nov 23, 2021 at 10:28:48PM -0500, Jason Merrill wrote:

Thanks.

+         while (true)
+           {
+             cp_expr expr (NULL_TREE);
+             /* Parse the next assignment-expression.  */
+             if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE))
+               {
+                 /* A braced-init-list.  */
+                 bool expr_nonconst_p;
+                 cp_lexer_set_source_position (parser->lexer);
+                 expr = cp_parser_braced_list (parser, &expr_nonconst_p);
+               }
+             else
+               expr = cp_parser_assignment_expression (parser);
+
+             /* If we have an ellipsis, then this is an expression
+                expansion.  */
+             if (cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS))
+               {
+                 /* Consume the `...'.  */
+                 cp_lexer_consume_token (parser->lexer);
+                 /* Build the argument pack.  */
+                 expr = make_pack_expansion (expr);
+               }
+
+             if (expr == error_mark_node)
+               index = error_mark_node;
+             else if (expression_list.get () == NULL
+                      && !PACK_EXPANSION_P (expr.get_value ()))
+               index = expr.get_value ();
+             else
+               vec_safe_push (expression_list, expr.get_value ());
+
+             /* If the next token isn't a `,', then we are done.  */
+             if (cp_lexer_next_token_is_not (parser->lexer, CPP_COMMA))
+               break;
+
+             if (expression_list.get () == NULL && index != error_mark_node)
+               {
+                 *&expression_list = make_tree_vector_single (index);
+                 index = NULL_TREE;
+               }
+
+             /* Otherwise, consume the `,' and keep going.  */
+             cp_lexer_consume_token (parser->lexer);
+           }

Let's share this loop with cp_parser_parenthesized_expression_list.

I'd prefer not to share the loop as whole because what exactly is done with
the parsed expressions differs a lot, for the array refs I'd prefer not to
push anything into a vector for the most common case with a single element.
I've outlined into a function what I think I can easily share
(see cp_parser_parenthesized_expression_list_elt in the updated patch).

+         if (expression_list.get () && index == error_mark_node)
+           {
+             release_tree_vector (*&expression_list);
+             *&expression_list = NULL;

This should probably become a release() method in releasing_vec.

Done.

+             FOR_EACH_VEC_SAFE_ELT (*index_exp_list, i, e)

This is build_x_compound_expr_from_vec.

Done 2x.

+/* Wrapper for above.  */

I just applied my auto_cond_timevar patch, so you can use that instead of
the wrapper.

Done.

+         for (i = 0; i < nargs; ++i)
+           {
+             tree arg = CALL_EXPR_ARG (c, i);
+
+             if (!PACK_EXPANSION_P (arg))
+               vec_safe_push (index_exp_list, RECUR (arg));
+             else
+               {
+                 /* Expand the pack expansion and push each entry onto
+                    INDEX_EXP_LIST.  */
+                 arg = tsubst_pack_expansion (arg, args, complain, in_decl);
+                 if (TREE_CODE (arg) == TREE_VEC)
+                   {
+                     unsigned int len, j;
+
+                     len = TREE_VEC_LENGTH (arg);
+                     for (j = 0; j < len; ++j)
+                       {
+                         tree value = TREE_VEC_ELT (arg, j);
+                         if (value != NULL_TREE)
+                           value = convert_from_reference (value);
+                         vec_safe_push (index_exp_list, value);
+                       }
+                   }
+                 else
+                   {
+                     /* A partial substitution.  Add one entry.  */
+                     vec_safe_push (index_exp_list, arg);
+                   }
+               }
+           }

Let's share this code with CALL_EXPR instead of duplicating it.

Done as tsubst_copy_and_build_call_args.

Tested on the new testcases so far, ok for trunk if it passes full
bootstrap/regtest?

OK.

2021-11-24  Jakub Jelinek  <ja...@redhat.com>

        PR c++/102611
gcc/
        * doc/invoke.texi (-Wcomma-subscript): Document that for
        -std=c++20 the option isn't enabled by default with -Wno-deprecated
        but for -std=c++23 it is.
gcc/c-family/
        * c-opts.c (c_common_post_options): Enable -Wcomma-subscript by
        default for C++23 regardless of warn_deprecated.
        * c-cppbuiltin.c (c_cpp_builtins): Predefine
        __cpp_multidimensional_subscript=202110L for C++23.
gcc/cp/
        * cp-tree.h (build_op_subscript): Implement P2128R6
        - Multidimensional subscript operator.  Declare.
        (class releasing_vec): Add release method.
        (grok_array_decl): Remove bool argument, add vec<tree, va_gc> **
        and tsubst_flags_t arguments.
        (build_min_non_dep_op_overload): Declare another overload.
        * parser.c (cp_parser_parenthesized_expression_list_elt): New function.
        (cp_parser_postfix_open_square_expression): Mention C++23 syntax in
        function comment.  For C++23 parse zero or more than one initializer
        clauses in expression list, adjust grok_array_decl caller.
        (cp_parser_parenthesized_expression_list): Use
        cp_parser_parenthesized_expression_list_elt.
        (cp_parser_builtin_offsetof): Adjust grok_array_decl caller.
        * decl.c (grok_op_properties): For C++23 don't check number
        of arguments of operator[].
        * decl2.c (grok_array_decl): Remove decltype_p argument, add
        index_exp_list and complain arguments.  If index_exp is NULL,
        handle *index_exp_list as the subscript expression list.
        * tree.c (build_min_non_dep_op_overload): New overload.
        * call.c (add_operator_candidates, build_over_call): Adjust comments
        for removal of build_new_op_1.
        (build_op_subscript): New function.
        * pt.c (tsubst_copy_and_build_call_args): New function.
        (tsubst_copy_and_build) <case ARRAY_REF>: If second
        operand is magic CALL_EXPR with ovl_op_identifier (ARRAY_REF)
        as CALL_EXPR_FN, tsubst CALL_EXPR arguments including expanding
        pack expressions in it and call grok_array_decl instead of
        build_x_array_ref.
        <case CALL_EXPR>: Use tsubst_copy_and_build_call_args.
        * semantics.c (handle_omp_array_sections_1): Adjust grok_array_decl
        caller.
gcc/testsuite/
        * g++.dg/cpp2a/comma1.C: Expect different diagnostics for C++23.
        * g++.dg/cpp2a/comma3.C: Likewise.
        * g++.dg/cpp2a/comma4.C: Expect diagnostics for C++23.
        * g++.dg/cpp2a/comma5.C: Expect different diagnostics for C++23.
        * g++.dg/cpp23/feat-cxx2b.C: Test __cpp_multidimensional_subscript
        predefined macro.
        * g++.dg/cpp23/subscript1.C: New test.
        * g++.dg/cpp23/subscript2.C: New test.
        * g++.dg/cpp23/subscript3.C: New test.
        * g++.dg/cpp23/subscript4.C: New test.
        * g++.dg/cpp23/subscript5.C: New test.
        * g++.dg/cpp23/subscript6.C: New test.

--- gcc/doc/invoke.texi.jj      2021-11-24 09:54:11.537738422 +0100
+++ gcc/doc/invoke.texi 2021-11-24 12:40:36.691189235 +0100
@@ -3479,19 +3479,27 @@ about ABI tags.
  @opindex Wcomma-subscript
  @opindex Wno-comma-subscript
  Warn about uses of a comma expression within a subscripting expression.
-This usage was deprecated in C++20.  However, a comma expression wrapped
-in @code{( )} is not deprecated.  Example:
+This usage was deprecated in C++20 and is going to be removed in C++23.
+However, a comma expression wrapped in @code{( )} is not deprecated.  Example:
@smallexample
  @group
  void f(int *a, int b, int c) @{
-    a[b,c];     // deprecated
+    a[b,c];     // deprecated in C++20, invalid in C++23
      a[(b,c)];   // OK
  @}
  @end group
  @end smallexample
-Enabled by default with @option{-std=c++20}.
+In C++23 it is valid to have comma separated expressions in a subscript
+when an overloaded subscript operator is found and supports the right
+number and types of arguments.  G++ will accept the formerly valid syntax
+for code that is not valid in C++23 but used to be valid but deprecated
+in C++20 with a pedantic warning that can be disabled with
+@option{-Wno-comma-subscript}.
+
+Enabled by default with @option{-std=c++20} unless @option{-Wno-deprecated},
+and with @option{-std=c++23} regardless of @option{-Wno-deprecated}.
@item -Wctad-maybe-unsupported @r{(C++ and Objective-C++ only)}
  @opindex Wctad-maybe-unsupported
--- gcc/c-family/c-opts.c.jj    2021-11-16 10:01:31.314758120 +0100
+++ gcc/c-family/c-opts.c       2021-11-24 12:40:36.692189221 +0100
@@ -946,7 +946,8 @@ c_common_post_options (const char **pfil
    /* -Wcomma-subscript is enabled by default in C++20.  */
    SET_OPTION_IF_UNSET (&global_options, &global_options_set,
                       warn_comma_subscript,
-                      cxx_dialect >= cxx20 && warn_deprecated);
+                      cxx_dialect >= cxx23
+                      || (cxx_dialect == cxx20 && warn_deprecated));
/* -Wvolatile is enabled by default in C++20. */
    SET_OPTION_IF_UNSET (&global_options, &global_options_set, warn_volatile,
--- gcc/c-family/c-cppbuiltin.c.jj      2021-11-16 10:01:31.314758120 +0100
+++ gcc/c-family/c-cppbuiltin.c 2021-11-24 12:40:36.692189221 +0100
@@ -1073,6 +1073,7 @@ c_cpp_builtins (cpp_reader *pfile)
          cpp_define (pfile, "__cpp_size_t_suffix=202011L");
          cpp_define (pfile, "__cpp_if_consteval=202106L");
          cpp_define (pfile, "__cpp_constexpr=202110L");
+         cpp_define (pfile, "__cpp_multidimensional_subscript=202110L");
        }
        if (flag_concepts)
          {
--- gcc/cp/cp-tree.h.jj 2021-11-24 09:54:11.457739563 +0100
+++ gcc/cp/cp-tree.h    2021-11-24 13:06:41.083861782 +0100
@@ -1007,7 +1007,9 @@ public:
       (bootstrap/91828).  */
    tree& operator[] (ptrdiff_t i) const { return (*v)[i]; }
- ~releasing_vec() { release_tree_vector (v); }
+  void release () { release_tree_vector (v); v = NULL; }
+
+  ~releasing_vec () { release_tree_vector (v); }
  private:
    vec_t *v;
  };
@@ -6471,6 +6473,9 @@ inline tree build_new_op (const op_locat
  }
  extern tree build_op_call                     (tree, vec<tree, va_gc> **,
                                                 tsubst_flags_t);
+extern tree build_op_subscript                 (const op_location_t &, tree,
+                                                vec<tree, va_gc> **, tree *,
+                                                tsubst_flags_t);
  extern bool aligned_allocation_fn_p           (tree);
  extern tree destroying_delete_p                       (tree);
  extern bool usual_deallocation_fn_p           (tree);
@@ -6813,7 +6818,8 @@ extern void maybe_make_one_only                   (tree)
  extern bool vague_linkage_p                   (tree);
  extern void grokclassfn                               (tree, tree,
                                                 enum overload_flags);
-extern tree grok_array_decl                    (location_t, tree, tree, bool);
+extern tree grok_array_decl                    (location_t, tree, tree,
+                                                vec<tree, va_gc> **, 
tsubst_flags_t);
  extern tree delete_sanity                     (location_t, tree, tree, bool,
                                                 int, tsubst_flags_t);
  extern tree check_classfn                     (tree, tree, tree);
@@ -7711,6 +7717,8 @@ extern tree build_min_nt_loc                      
(location
                                                 ...);
  extern tree build_min_non_dep                 (enum tree_code, tree, ...);
  extern tree build_min_non_dep_op_overload     (enum tree_code, tree, tree, 
...);
+extern tree build_min_non_dep_op_overload      (tree, tree, tree,
+                                                vec<tree, va_gc> *);
  extern tree build_min_nt_call_vec (tree, vec<tree, va_gc> *);
  extern tree build_min_non_dep_call_vec                (tree, tree, vec<tree, 
va_gc> *);
  extern vec<tree, va_gc>* vec_copy_and_insert    (vec<tree, va_gc>*, tree, 
unsigned);
--- gcc/cp/parser.c.jj  2021-11-24 09:54:11.497738993 +0100
+++ gcc/cp/parser.c     2021-11-24 14:11:28.599152722 +0100
@@ -7898,11 +7898,62 @@ cp_parser_postfix_expression (cp_parser
    return error_mark_node;
  }
+/* Helper function for cp_parser_parenthesized_expression_list and
+   cp_parser_postfix_open_square_expression.  Parse a single element
+   of parenthesized expression list.  */
+
+static cp_expr
+cp_parser_parenthesized_expression_list_elt (cp_parser *parser, bool cast_p,
+                                            bool allow_expansion_p,
+                                            bool fold_expr_p,
+                                            bool *non_constant_p)
+{
+  cp_expr expr (NULL_TREE);
+  bool expr_non_constant_p;
+
+  /* Parse the next assignment-expression.  */
+  if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE))
+    {
+      /* A braced-init-list.  */
+      cp_lexer_set_source_position (parser->lexer);
+      maybe_warn_cpp0x (CPP0X_INITIALIZER_LISTS);
+      expr = cp_parser_braced_list (parser, &expr_non_constant_p);
+      if (non_constant_p && expr_non_constant_p)
+       *non_constant_p = true;
+    }
+  else if (non_constant_p)
+    {
+      expr = cp_parser_constant_expression (parser,
+                                           /*allow_non_constant_p=*/true,
+                                           &expr_non_constant_p);
+      if (expr_non_constant_p)
+       *non_constant_p = true;
+    }
+  else
+    expr = cp_parser_assignment_expression (parser, /*pidk=*/NULL, cast_p);
+
+  if (fold_expr_p)
+    expr = instantiate_non_dependent_expr (expr);
+
+  /* If we have an ellipsis, then this is an expression expansion.  */
+  if (allow_expansion_p
+      && cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS))
+    {
+      /* Consume the `...'.  */
+      cp_lexer_consume_token (parser->lexer);
+
+      /* Build the argument pack.  */
+      expr = make_pack_expansion (expr);
+    }
+  return expr;
+}
+
  /* A subroutine of cp_parser_postfix_expression that also gets hijacked
     by cp_parser_builtin_offsetof.  We're looking for
postfix-expression [ expression ]
       postfix-expression [ braced-init-list ] (C++11)
+     postfix-expression [ expression-list[opt] ] (C++23)
FOR_OFFSETOF is set if we're being called in that context, which
     changes how we deal with integer constant expressions.  */
@@ -7914,6 +7965,7 @@ cp_parser_postfix_open_square_expression
                                          bool decltype_p)
  {
    tree index = NULL_TREE;
+  releasing_vec expression_list = NULL;
    location_t loc = cp_lexer_peek_token (parser->lexer)->location;
    bool saved_greater_than_is_operator_p;
@@ -7935,7 +7987,49 @@ cp_parser_postfix_open_square_expression
      index = cp_parser_constant_expression (parser);
    else
      {
-      if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE))
+      if (cxx_dialect >= cxx23
+         && cp_lexer_next_token_is (parser->lexer, CPP_CLOSE_SQUARE))
+       *&expression_list = make_tree_vector ();
+      else if (cxx_dialect >= cxx23)
+       {
+         while (true)
+           {
+             cp_expr expr
+               = cp_parser_parenthesized_expression_list_elt (parser,
+                                                              /*cast_p=*/
+                                                              false,
+                                                              /*allow_exp_p=*/
+                                                              true,
+                                                              /*fold_expr_p=*/
+                                                              false,
+                                                              /*non_cst_p=*/
+                                                              NULL);
+
+             if (expr == error_mark_node)
+               index = error_mark_node;
+             else if (expression_list.get () == NULL
+                      && !PACK_EXPANSION_P (expr.get_value ()))
+               index = expr.get_value ();
+             else
+               vec_safe_push (expression_list, expr.get_value ());
+
+             /* If the next token isn't a `,', then we are done.  */
+             if (cp_lexer_next_token_is_not (parser->lexer, CPP_COMMA))
+               break;
+
+             if (expression_list.get () == NULL && index != error_mark_node)
+               {
+                 *&expression_list = make_tree_vector_single (index);
+                 index = NULL_TREE;
+               }
+
+             /* Otherwise, consume the `,' and keep going.  */
+             cp_lexer_consume_token (parser->lexer);
+           }
+         if (expression_list.get () && index == error_mark_node)
+           expression_list.release ();
+       }
+      else if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE))
        {
          bool expr_nonconst_p;
          cp_lexer_set_source_position (parser->lexer);
@@ -7955,7 +8049,9 @@ cp_parser_postfix_open_square_expression
/* Build the ARRAY_REF. */
    postfix_expression = grok_array_decl (loc, postfix_expression,
-                                       index, decltype_p);
+                                       index, &expression_list,
+                                       tf_warning_or_error
+                                       | (decltype_p ? tf_decltype : 0));
/* When not doing offsetof, array references are not permitted in
       constant-expressions.  */
@@ -8315,44 +8411,11 @@ cp_parser_parenthesized_expression_list
          }
        else
          {
-           bool expr_non_constant_p;
-
-           /* Parse the next assignment-expression.  */
-           if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE))
-             {
-               /* A braced-init-list.  */
-               cp_lexer_set_source_position (parser->lexer);
-               maybe_warn_cpp0x (CPP0X_INITIALIZER_LISTS);
-               expr = cp_parser_braced_list (parser, &expr_non_constant_p);
-               if (non_constant_p && expr_non_constant_p)
-                 *non_constant_p = true;
-             }
-           else if (non_constant_p)
-             {
-               expr = (cp_parser_constant_expression
-                       (parser, /*allow_non_constant_p=*/true,
-                        &expr_non_constant_p));
-               if (expr_non_constant_p)
-                 *non_constant_p = true;
-             }
-           else
-             expr = cp_parser_assignment_expression (parser, /*pidk=*/NULL,
-                                                     cast_p);
-
-           if (fold_expr_p)
-             expr = instantiate_non_dependent_expr (expr);
-
-            /* If we have an ellipsis, then this is an expression
-              expansion.  */
-            if (allow_expansion_p
-                && cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS))
-              {
-                /* Consume the `...'.  */
-                cp_lexer_consume_token (parser->lexer);
-
-                /* Build the argument pack.  */
-                expr = make_pack_expansion (expr);
-              }
+           expr
+             = cp_parser_parenthesized_expression_list_elt (parser, cast_p,
+                                                            allow_expansion_p,
+                                                            fold_expr_p,
+                                                            non_constant_p);
if (wrap_locations_p)
              expr.maybe_add_location_wrapper ();
@@ -10625,8 +10688,8 @@ cp_parser_builtin_offsetof (cp_parser *p
case CPP_DEREF:
          /* offsetof-member-designator "->" identifier */
-         expr = grok_array_decl (token->location, expr,
-                                 integer_zero_node, false);
+         expr = grok_array_decl (token->location, expr, integer_zero_node,
+                                 NULL, tf_warning_or_error);
          /* FALLTHRU */
case CPP_DOT:
--- gcc/cp/decl.c.jj    2021-11-24 09:54:11.474739321 +0100
+++ gcc/cp/decl.c       2021-11-24 12:40:36.699189121 +0100
@@ -15140,6 +15140,8 @@ grok_op_properties (tree decl, bool comp
      case OVL_OP_FLAG_BINARY:
        if (arity != 2)
        {
+         if (operator_code == ARRAY_REF && cxx_dialect >= cxx23)
+           break;
          error_at (loc,
                    methodp
                    ? G_("%qD must have exactly one argument")
--- gcc/cp/decl2.c.jj   2021-11-05 00:43:22.511625568 +0100
+++ gcc/cp/decl2.c      2021-11-24 13:15:41.845142933 +0100
@@ -363,16 +363,20 @@ grokclassfn (tree ctype, tree function,
  }
/* Create an ARRAY_REF, checking for the user doing things backwards
-   along the way.  DECLTYPE_P is for N3276, as in the parser.  */
+   along the way.
+   If INDEX_EXP is non-NULL, then that is the index expression,
+   otherwise INDEX_EXP_LIST is the list of index expressions.  */
tree
  grok_array_decl (location_t loc, tree array_expr, tree index_exp,
-                bool decltype_p)
+                vec<tree, va_gc> **index_exp_list, tsubst_flags_t complain)
  {
    tree type;
    tree expr;
    tree orig_array_expr = array_expr;
    tree orig_index_exp = index_exp;
+  vec<tree, va_gc> *orig_index_exp_list
+    = index_exp_list ? *index_exp_list : NULL;
    tree overload = NULL_TREE;
if (error_operand_p (array_expr) || error_operand_p (index_exp))
@@ -381,11 +385,23 @@ grok_array_decl (location_t loc, tree ar
    if (processing_template_decl)
      {
        if (type_dependent_expression_p (array_expr)
-         || type_dependent_expression_p (index_exp))
-       return build_min_nt_loc (loc, ARRAY_REF, array_expr, index_exp,
-                                NULL_TREE, NULL_TREE);
+         || (index_exp ? type_dependent_expression_p (index_exp)
+                       : any_type_dependent_arguments_p (*index_exp_list)))
+       {
+         if (index_exp == NULL)
+           index_exp = build_min_nt_call_vec (ovl_op_identifier (ARRAY_REF),
+                                              *index_exp_list);
+         return build_min_nt_loc (loc, ARRAY_REF, array_expr, index_exp,
+                                  NULL_TREE, NULL_TREE);
+       }
        array_expr = build_non_dependent_expr (array_expr);
-      index_exp = build_non_dependent_expr (index_exp);
+      if (index_exp)
+       index_exp = build_non_dependent_expr (index_exp);
+      else
+       {
+         orig_index_exp_list = make_tree_vector_copy (*index_exp_list);
+         make_args_non_dependent (*index_exp_list);
+       }
      }
type = TREE_TYPE (array_expr);
@@ -393,13 +409,44 @@ grok_array_decl (location_t loc, tree ar
    type = non_reference (type);
/* If they have an `operator[]', use that. */
-  if (MAYBE_CLASS_TYPE_P (type) || MAYBE_CLASS_TYPE_P (TREE_TYPE (index_exp)))
+  if (MAYBE_CLASS_TYPE_P (type)
+      || (index_exp && MAYBE_CLASS_TYPE_P (TREE_TYPE (index_exp)))
+      || (index_exp == NULL_TREE
+         && !(*index_exp_list)->is_empty ()
+         && MAYBE_CLASS_TYPE_P (TREE_TYPE ((*index_exp_list)->last ()))))
      {
-      tsubst_flags_t complain = tf_warning_or_error;
-      if (decltype_p)
-       complain |= tf_decltype;
-      expr = build_new_op (loc, ARRAY_REF, LOOKUP_NORMAL, array_expr,
-                          index_exp, NULL_TREE, &overload, complain);
+      if (index_exp)
+       expr = build_new_op (loc, ARRAY_REF, LOOKUP_NORMAL, array_expr,
+                            index_exp, NULL_TREE, &overload, complain);
+      else if ((*index_exp_list)->is_empty ())
+       expr = build_op_subscript (loc, array_expr, index_exp_list, &overload,
+                                  complain);
+      else
+       {
+         expr = build_op_subscript (loc, array_expr, index_exp_list,
+                                    &overload, complain & tf_decltype);
+         if (expr == error_mark_node)
+           {
+             tree idx = build_x_compound_expr_from_vec (*index_exp_list, NULL,
+                                                        tf_none);
+             if (idx != error_mark_node)
+               expr = build_new_op (loc, ARRAY_REF, LOOKUP_NORMAL, array_expr,
+                                    idx, NULL_TREE, &overload,
+                                    complain & tf_decltype);
+             if (expr == error_mark_node)
+               {
+                 overload = NULL_TREE;
+                 expr = build_op_subscript (loc, array_expr, index_exp_list,
+                                            &overload, complain);
+               }
+             else
+               /* If it would be valid albeit deprecated expression in C++20,
+                  just pedwarn on it and treat it as if wrapped in ().  */
+               pedwarn (loc, OPT_Wcomma_subscript,
+                        "top-level comma expression in array subscript "
+                        "changed meaning in C++23");
+           }
+       }
      }
    else
      {
@@ -415,6 +462,31 @@ grok_array_decl (location_t loc, tree ar
        else
        p1 = build_expr_type_conversion (WANT_POINTER, array_expr, false);
+ if (index_exp == NULL_TREE)
+       {
+         if ((*index_exp_list)->is_empty ())
+           {
+             error_at (loc, "built-in subscript operator without expression "
+                            "list");
+             return error_mark_node;
+           }
+         tree idx = build_x_compound_expr_from_vec (*index_exp_list, NULL,
+                                                    tf_none);
+         if (idx != error_mark_node)
+           /* If it would be valid albeit deprecated expression in C++20,
+              just pedwarn on it and treat it as if wrapped in ().  */
+           pedwarn (loc, OPT_Wcomma_subscript,
+                    "top-level comma expression in array subscript "
+                    "changed meaning in C++23");
+         else
+           {
+             error_at (loc, "built-in subscript operator with more than one "
+                            "expression in expression list");
+             return error_mark_node;
+           }
+         index_exp = idx;
+       }
+
        if (TREE_CODE (TREE_TYPE (index_exp)) == ARRAY_TYPE)
        p2 = index_exp;
        else
@@ -457,11 +529,30 @@ grok_array_decl (location_t loc, tree ar
    if (processing_template_decl && expr != error_mark_node)
      {
        if (overload != NULL_TREE)
-       return (build_min_non_dep_op_overload
-               (ARRAY_REF, expr, overload, orig_array_expr, orig_index_exp));
+       {
+         if (orig_index_exp == NULL_TREE)
+           {
+             expr = build_min_non_dep_op_overload (expr, overload,
+                                                   orig_array_expr,
+                                                   orig_index_exp_list);
+             release_tree_vector (orig_index_exp_list);
+             return expr;
+           }
+         return build_min_non_dep_op_overload (ARRAY_REF, expr, overload,
+                                               orig_array_expr,
+                                               orig_index_exp);
+       }
+
+      if (orig_index_exp == NULL_TREE)
+       {
+         orig_index_exp
+           = build_min_nt_call_vec (ovl_op_identifier (ARRAY_REF),
+                                    orig_index_exp_list);
+         release_tree_vector (orig_index_exp_list);
+       }
- return build_min_non_dep (ARRAY_REF, expr, orig_array_expr, orig_index_exp,
-                               NULL_TREE, NULL_TREE);
+      return build_min_non_dep (ARRAY_REF, expr, orig_array_expr,
+                               orig_index_exp, NULL_TREE, NULL_TREE);
      }
    return expr;
  }
--- gcc/cp/tree.c.jj    2021-11-19 16:39:51.534595887 +0100
+++ gcc/cp/tree.c       2021-11-24 12:40:36.700189107 +0100
@@ -3671,13 +3671,42 @@ build_min_non_dep_op_overload (enum tree
        }
      }
    else
-   gcc_unreachable ();
+    gcc_unreachable ();
va_end (p);
    call = build_min_non_dep_call_vec (non_dep, fn, args);
tree call_expr = extract_call_expr (call);
    KOENIG_LOOKUP_P (call_expr) = KOENIG_LOOKUP_P (non_dep);
+  CALL_EXPR_OPERATOR_SYNTAX (call_expr) = true;
+  CALL_EXPR_ORDERED_ARGS (call_expr) = CALL_EXPR_ORDERED_ARGS (non_dep);
+  CALL_EXPR_REVERSE_ARGS (call_expr) = CALL_EXPR_REVERSE_ARGS (non_dep);
+
+  return call;
+}
+
+/* Similar to above build_min_non_dep_op_overload, but arguments
+   are taken from ARGS vector.  */
+
+tree
+build_min_non_dep_op_overload (tree non_dep, tree overload, tree object,
+                              vec<tree, va_gc> *args)
+{
+  non_dep = extract_call_expr (non_dep);
+
+  unsigned int nargs = call_expr_nargs (non_dep);
+  gcc_assert (TREE_CODE (TREE_TYPE (overload)) == METHOD_TYPE);
+  tree binfo = TYPE_BINFO (TREE_TYPE (object));
+  tree method = build_baselink (binfo, binfo, overload, NULL_TREE);
+  tree fn = build_min (COMPONENT_REF, TREE_TYPE (overload),
+                      object, method, NULL_TREE);
+  nargs--;
+  gcc_assert (vec_safe_length (args) == nargs);
+
+  tree call = build_min_non_dep_call_vec (non_dep, fn, args);
+
+  tree call_expr = extract_call_expr (call);
+  KOENIG_LOOKUP_P (call_expr) = KOENIG_LOOKUP_P (non_dep);
    CALL_EXPR_OPERATOR_SYNTAX (call_expr) = true;
    CALL_EXPR_ORDERED_ARGS (call_expr) = CALL_EXPR_ORDERED_ARGS (non_dep);
    CALL_EXPR_REVERSE_ARGS (call_expr) = CALL_EXPR_REVERSE_ARGS (non_dep);
--- gcc/cp/call.c.jj    2021-11-24 09:54:11.456739577 +0100
+++ gcc/cp/call.c       2021-11-24 14:11:02.262530064 +0100
@@ -6283,7 +6283,7 @@ op_is_ordered (tree_code code)
      }
  }
-/* Subroutine of build_new_op_1: Add to CANDIDATES all candidates for the
+/* Subroutine of build_new_op: Add to CANDIDATES all candidates for the
     operator indicated by CODE/CODE2.  This function calls itself recursively 
to
     handle C++20 rewritten comparison operator candidates.  */
@@ -6932,6 +6932,117 @@ build_new_op (const op_location_t &loc,
    return NULL_TREE;
  }
+/* Build a new call to operator[]. This may change ARGS. */
+
+tree
+build_op_subscript (const op_location_t &loc, tree obj,
+                   vec<tree, va_gc> **args, tree *overload,
+                   tsubst_flags_t complain)
+{
+  struct z_candidate *candidates = 0, *cand;
+  tree fns, first_mem_arg = NULL_TREE;
+  bool any_viable_p;
+  tree result = NULL_TREE;
+  void *p;
+
+  auto_cond_timevar tv (TV_OVERLOAD);
+
+  obj = mark_lvalue_use (obj);
+
+  if (error_operand_p (obj))
+    return error_mark_node;
+
+  tree type = TREE_TYPE (obj);
+
+  obj = prep_operand (obj);
+
+  if (TYPE_BINFO (type))
+    {
+      fns = lookup_fnfields (TYPE_BINFO (type), ovl_op_identifier (ARRAY_REF),
+                            1, complain);
+      if (fns == error_mark_node)
+       return error_mark_node;
+    }
+  else
+    fns = NULL_TREE;
+
+  if (args != NULL && *args != NULL)
+    {
+      *args = resolve_args (*args, complain);
+      if (*args == NULL)
+       return error_mark_node;
+    }
+
+  /* Get the high-water mark for the CONVERSION_OBSTACK.  */
+  p = conversion_obstack_alloc (0);
+
+  if (fns)
+    {
+      first_mem_arg = obj;
+
+      add_candidates (BASELINK_FUNCTIONS (fns),
+                     first_mem_arg, *args, NULL_TREE,
+                     NULL_TREE, false,
+                     BASELINK_BINFO (fns), BASELINK_ACCESS_BINFO (fns),
+                     LOOKUP_NORMAL, &candidates, complain);
+    }
+
+  /* Be strict here because if we choose a bad conversion candidate, the
+     errors we get won't mention the call context.  */
+  candidates = splice_viable (candidates, true, &any_viable_p);
+  if (!any_viable_p)
+    {
+      if (complain & tf_error)
+       {
+         auto_diagnostic_group d;
+         error ("no match for call to %<%T::operator[] (%A)%>",
+                TREE_TYPE (obj), build_tree_list_vec (*args));
+         print_z_candidates (loc, candidates);
+       }
+      result = error_mark_node;
+    }
+  else
+    {
+      cand = tourney (candidates, complain);
+      if (cand == 0)
+       {
+         if (complain & tf_error)
+           {
+             auto_diagnostic_group d;
+             error ("call of %<%T::operator[] (%A)%> is ambiguous",
+                    TREE_TYPE (obj), build_tree_list_vec (*args));
+             print_z_candidates (loc, candidates);
+           }
+         result = error_mark_node;
+       }
+      else if (TREE_CODE (cand->fn) == FUNCTION_DECL
+              && DECL_OVERLOADED_OPERATOR_P (cand->fn)
+              && DECL_OVERLOADED_OPERATOR_IS (cand->fn, ARRAY_REF))
+       {
+         if (overload)
+           *overload = cand->fn;
+         result = build_over_call (cand, LOOKUP_NORMAL, complain);
+         if (trivial_fn_p (cand->fn) || DECL_IMMEDIATE_FUNCTION_P (cand->fn))
+           /* There won't be a CALL_EXPR.  */;
+         else if (result && result != error_mark_node)
+           {
+             tree call = extract_call_expr (result);
+             CALL_EXPR_OPERATOR_SYNTAX (call) = true;
+
+             /* Specify evaluation order as per P0145R2.  */
+             CALL_EXPR_ORDERED_ARGS (call) = op_is_ordered (ARRAY_REF) == 1;
+           }
+       }
+      else
+       gcc_unreachable ();
+    }
+
+  /* Free all the conversions we allocated.  */
+  obstack_free (&conversion_obstack, p);
+
+  return result;
+}
+
  /* CALL was returned by some call-building function; extract the actual
     CALL_EXPR from any bits that have been tacked on, e.g. by
     convert_from_reference.  */
@@ -9748,7 +9859,7 @@ build_over_call (struct z_candidate *can
    if (cand->flags & LOOKUP_LIST_INIT_CTOR)
      {
        tree c = extract_call_expr (call);
-      /* build_new_op_1 will clear this when appropriate.  */
+      /* build_new_op will clear this when appropriate.  */
        CALL_EXPR_ORDERED_ARGS (c) = true;
      }
    if (warned_p)
--- gcc/cp/pt.c.jj      2021-11-23 15:09:15.633870147 +0100
+++ gcc/cp/pt.c 2021-11-24 13:49:45.607858720 +0100
@@ -19654,6 +19654,49 @@ maybe_fold_fn_template_args (tree fn, ts
    return fold_targs_r (targs, complain);
  }
+/* Helper function for tsubst_copy_and_build CALL_EXPR and ARRAY_REF
+   handling.  */
+
+static void
+tsubst_copy_and_build_call_args (tree t, tree args, tsubst_flags_t complain,
+                                tree in_decl,
+                                bool integral_constant_expression_p,
+                                releasing_vec &call_args)
+{
+  unsigned int nargs = call_expr_nargs (t);
+  for (unsigned int i = 0; i < nargs; ++i)
+    {
+      tree arg = CALL_EXPR_ARG (t, i);
+
+      if (!PACK_EXPANSION_P (arg))
+       vec_safe_push (call_args,
+                      tsubst_copy_and_build (arg, args, complain, in_decl,
+                                             /*function_p=*/false,
+                                             integral_constant_expression_p));
+      else
+       {
+         /* Expand the pack expansion and push each entry onto CALL_ARGS.  */
+         arg = tsubst_pack_expansion (arg, args, complain, in_decl);
+         if (TREE_CODE (arg) == TREE_VEC)
+           {
+             unsigned int len, j;
+
+             len = TREE_VEC_LENGTH (arg);
+             for (j = 0; j < len; ++j)
+               {
+                 tree value = TREE_VEC_ELT (arg, j);
+                 if (value != NULL_TREE)
+                   value = convert_from_reference (value);
+                 vec_safe_push (call_args, value);
+               }
+           }
+         else
+           /* A partial substitution.  Add one entry.  */
+           vec_safe_push (call_args, arg);
+       }
+    }
+}
+
  /* Like tsubst but deals with expressions and performs semantic
     analysis.  FUNCTION_P is true if T is the "F" in "F (ARGS)" or
     "F<TARGS> (ARGS)".  */
@@ -20053,6 +20096,28 @@ tsubst_copy_and_build (tree t,
      case ARRAY_REF:
        op1 = tsubst_non_call_postfix_expression (TREE_OPERAND (t, 0),
                                                args, complain, in_decl);
+      if (TREE_CODE (TREE_OPERAND (t, 1)) == CALL_EXPR
+         && (CALL_EXPR_FN (TREE_OPERAND (t, 1))
+             == ovl_op_identifier (ARRAY_REF)))
+       {
+         tree c = TREE_OPERAND (t, 1);
+         releasing_vec index_exp_list;
+         tsubst_copy_and_build_call_args (c, args, complain, in_decl,
+                                          integral_constant_expression_p,
+                                          index_exp_list);
+
+         tree r;
+         if (vec_safe_length (index_exp_list) == 1
+             && !PACK_EXPANSION_P (index_exp_list[0]))
+           r = grok_array_decl (EXPR_LOCATION (t), op1,
+                                index_exp_list[0], NULL,
+                                complain | decltype_flag);
+         else
+           r = grok_array_decl (EXPR_LOCATION (t), op1,
+                                NULL_TREE, &index_exp_list,
+                                complain | decltype_flag);
+         RETURN (r);
+       }
        RETURN (build_x_array_ref (EXPR_LOCATION (t), op1,
                                 RECUR (TREE_OPERAND (t, 1)),
                                 complain|decltype_flag));
@@ -20261,7 +20326,7 @@ tsubst_copy_and_build (tree t,
      case CALL_EXPR:
        {
        tree function;
-       unsigned int nargs, i;
+       unsigned int nargs;
        bool qualified_p;
        bool koenig_p;
        tree ret;
@@ -20344,37 +20409,9 @@ tsubst_copy_and_build (tree t,
nargs = call_expr_nargs (t);
        releasing_vec call_args;
-       for (i = 0; i < nargs; ++i)
-         {
-           tree arg = CALL_EXPR_ARG (t, i);
-
-           if (!PACK_EXPANSION_P (arg))
-             vec_safe_push (call_args, RECUR (CALL_EXPR_ARG (t, i)));
-           else
-             {
-               /* Expand the pack expansion and push each entry onto
-                  CALL_ARGS.  */
-               arg = tsubst_pack_expansion (arg, args, complain, in_decl);
-               if (TREE_CODE (arg) == TREE_VEC)
-                 {
-                   unsigned int len, j;
-
-                   len = TREE_VEC_LENGTH (arg);
-                   for (j = 0; j < len; ++j)
-                     {
-                       tree value = TREE_VEC_ELT (arg, j);
-                       if (value != NULL_TREE)
-                         value = convert_from_reference (value);
-                       vec_safe_push (call_args, value);
-                     }
-                 }
-               else
-                 {
-                   /* A partial substitution.  Add one entry.  */
-                   vec_safe_push (call_args, arg);
-                 }
-             }
-         }
+       tsubst_copy_and_build_call_args (t, args, complain, in_decl,
+                                        integral_constant_expression_p,
+                                        call_args);
/* Stripped-down processing for a call in a thunk. Specifically, in
           the thunk template for a generic lambda.  */
--- gcc/cp/semantics.c.jj       2021-11-23 11:03:43.988325187 +0100
+++ gcc/cp/semantics.c  2021-11-24 12:40:36.705189036 +0100
@@ -5394,7 +5394,8 @@ handle_omp_array_sections_1 (tree c, tre
                      OMP_CLAUSE_CODE (c) == OMP_CLAUSE_REDUCTION
                      || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_IN_REDUCTION
                      || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_TASK_REDUCTION);
-  ret = grok_array_decl (OMP_CLAUSE_LOCATION (c), ret, low_bound, false);
+  ret = grok_array_decl (OMP_CLAUSE_LOCATION (c), ret, low_bound, NULL,
+                        tf_warning_or_error);
    return ret;
  }
--- gcc/testsuite/g++.dg/cpp2a/comma1.C.jj 2021-10-15 11:58:45.210130057 +0200
+++ gcc/testsuite/g++.dg/cpp2a/comma1.C 2021-11-24 12:40:37.107183299 +0100
@@ -8,19 +8,24 @@ struct S {
  void
  fn (int *a, int b, int c)
  {
-  a[b,c]; // { dg-warning "top-level comma expression in array subscript is deprecated" 
"" { target c++20 } }
+  a[b,c]; // { dg-warning "top-level comma expression in array subscript is deprecated" 
"" { target c++20_only } }
+         // { dg-error "top-level comma expression in array subscript changed meaning 
in" "" { target c++23 } .-1 }
    a[(b,c)];
- a[(void) b, c]; // { dg-warning "top-level comma expression in array subscript is deprecated" "" { target c++20 } }
+  a[(void) b, c]; // { dg-warning "top-level comma expression in array subscript is 
deprecated" "" { target c++20_only } }
+                 // { dg-error "top-level comma expression in array subscript changed meaning 
in" "" { target c++23 } .-1 }
    a[((void) b, c)];
- a[(void) b, (void) c, (void) b, b]; // { dg-warning "top-level comma expression in array subscript is deprecated" "" { target c++20 } }
+  a[(void) b, (void) c, (void) b, b]; // { dg-warning "top-level comma expression in array 
subscript is deprecated" "" { target c++20_only } }
+                                     // { dg-error "top-level comma expression in array 
subscript changed meaning in" "" { target c++23 } .-1 }
    a[((void) b, (void) c, (void) b, b)];
- a[S(), 10]; // { dg-warning "top-level comma expression in array subscript is deprecated" "" { target c++20 } }
+  a[S(), 10]; // { dg-warning "top-level comma expression in array subscript is 
deprecated" "" { target c++20_only } }
+             // { dg-error "top-level comma expression in array subscript changed meaning 
in" "" { target c++23 } .-1 }
    a[(S(), 10)];
a[int{(1,2)}];
-  a[int{(1,2)}, int{}]; // { dg-warning "top-level comma expression in array subscript is 
deprecated" "" { target c++20 } }
+  a[int{(1,2)}, int{}]; // { dg-warning "top-level comma expression in array subscript is 
deprecated" "" { target c++20_only } }
+                       // { dg-error "top-level comma expression in array subscript changed 
meaning in" "" { target c++23 } .-1 }
    a[(int{(1,2)}, int{})];
  }
--- gcc/testsuite/g++.dg/cpp2a/comma3.C.jj      2021-10-15 11:58:45.210130057 
+0200
+++ gcc/testsuite/g++.dg/cpp2a/comma3.C 2021-11-24 12:40:37.119183128 +0100
@@ -9,19 +9,24 @@ struct S {
  void
  fn (int *a, int b, int c)
  {
-  a[b,c]; // { dg-warning "top-level comma expression in array subscript is 
deprecated" }
+  a[b,c]; // { dg-warning "top-level comma expression in array subscript is deprecated" 
"" { target c++20_down } }
+         // { dg-warning "top-level comma expression in array subscript changed meaning 
in" "" { target c++23 } .-1 }
    a[(b,c)];
- a[(void) b, c]; // { dg-warning "top-level comma expression in array subscript is deprecated" }
+  a[(void) b, c]; // { dg-warning "top-level comma expression in array subscript is 
deprecated" "" { target c++20_down } }
+                 // { dg-warning "top-level comma expression in array subscript changed 
meaning in" "" { target c++23 } .-1 }
    a[((void) b, c)];
- a[(void) b, (void) c, (void) b, b]; // { dg-warning "top-level comma expression in array subscript is deprecated" }
+  a[(void) b, (void) c, (void) b, b]; // { dg-warning "top-level comma expression in array 
subscript is deprecated" "" { target c++20_down } }
+                                     // { dg-warning "top-level comma expression in array 
subscript changed meaning in" "" { target c++23 } .-1 }
    a[((void) b, (void) c, (void) b, b)];
- a[S(), 10]; // { dg-warning "top-level comma expression in array subscript is deprecated" }
+  a[S(), 10]; // { dg-warning "top-level comma expression in array subscript is 
deprecated" "" { target c++20_down } }
+             // { dg-warning "top-level comma expression in array subscript changed meaning 
in" "" { target c++23 } .-1 }
    a[(S(), 10)];
a[int{(1,2)}];
-  a[int{(1,2)}, int{}]; // { dg-warning "top-level comma expression in array 
subscript is deprecated" }
+  a[int{(1,2)}, int{}]; // { dg-warning "top-level comma expression in array subscript is 
deprecated" "" { target c++20_down } }
+                       // { dg-warning "top-level comma expression in array subscript changed 
meaning in" "" { target c++23 } .-1 }
    a[(int{(1,2)}, int{})];
  }
--- gcc/testsuite/g++.dg/cpp2a/comma4.C.jj      2021-10-15 11:58:45.210130057 
+0200
+++ gcc/testsuite/g++.dg/cpp2a/comma4.C 2021-11-24 12:40:37.122183085 +0100
@@ -10,18 +10,23 @@ void
  fn (int *a, int b, int c)
  {
    a[b,c]; // { dg-bogus "top-level comma expression in array subscript is 
deprecated" }
+         // { dg-warning "top-level comma expression in array subscript changed meaning 
in" "" { target c++23 } .-1 }
    a[(b,c)];
a[(void) b, c]; // { dg-bogus "top-level comma expression in array subscript is deprecated" }
+                 // { dg-warning "top-level comma expression in array subscript changed 
meaning in" "" { target c++23 } .-1 }
    a[((void) b, c)];
a[(void) b, (void) c, (void) b, b]; // { dg-bogus "top-level comma expression in array subscript is deprecated" }
+                                     // { dg-warning "top-level comma expression in array 
subscript changed meaning in" "" { target c++23 } .-1 }
    a[((void) b, (void) c, (void) b, b)];
a[S(), 10]; // { dg-bogus "top-level comma expression in array subscript is deprecated" }
+             // { dg-warning "top-level comma expression in array subscript changed meaning 
in" "" { target c++23 } .-1 }
    a[(S(), 10)];
a[int{(1,2)}];
    a[int{(1,2)}, int{}]; // { dg-bogus "top-level comma expression in array 
subscript is deprecated" }
+                       // { dg-warning "top-level comma expression in array subscript changed 
meaning in" "" { target c++23 } .-1 }
    a[(int{(1,2)}, int{})];
  }
--- gcc/testsuite/g++.dg/cpp2a/comma5.C.jj      2021-10-15 11:58:45.210130057 
+0200
+++ gcc/testsuite/g++.dg/cpp2a/comma5.C 2021-11-24 12:40:37.126183028 +0100
@@ -8,14 +8,20 @@ void
  fn (int *a, int b, int c)
  {
    a[foo<int, int>(1, 2)];
-  a[foo<int, int>(1, 2), foo<int, int>(3, 4)]; // { dg-warning "24:top-level comma 
expression in array subscript is deprecated" }
+  a[foo<int, int>(1, 2), foo<int, int>(3, 4)]; // { dg-warning "24:top-level comma expression 
in array subscript is deprecated" "" { target c++20_down } }
+                                              // { dg-error "top-level comma expression in 
array subscript changed meaning in" "" { target c++23 } .-1 }
- a[b < c, b < c]; // { dg-warning "top-level comma expression in array subscript is deprecated" }
-  a[b < c, b > c]; // { dg-warning "top-level comma expression in array subscript 
is deprecated" }
-  a[b > c, b > c]; // { dg-warning "top-level comma expression in array subscript 
is deprecated" }
-  a[b > c, b < c]; // { dg-warning "top-level comma expression in array subscript 
is deprecated" }
+  a[b < c, b < c]; // { dg-warning "top-level comma expression in array subscript is 
deprecated" "" { target c++20_down } }
+                  // { dg-error "top-level comma expression in array subscript changed 
meaning in" "" { target c++23 } .-1 }
+  a[b < c, b > c]; // { dg-warning "top-level comma expression in array subscript is 
deprecated" "" { target c++20_down } }
+                  // { dg-error "top-level comma expression in array subscript changed 
meaning in" "" { target c++23 } .-1 }
+  a[b > c, b > c]; // { dg-warning "top-level comma expression in array subscript is 
deprecated" "" { target c++20_down } }
+                  // { dg-error "top-level comma expression in array subscript changed 
meaning in" "" { target c++23 } .-1 }
+  a[b > c, b < c]; // { dg-warning "top-level comma expression in array subscript is 
deprecated" "" { target c++20_down } }
+                  // { dg-error "top-level comma expression in array subscript changed 
meaning in" "" { target c++23 } .-1 }
    a[(b < c, b < c)];
    a[(b < c, b > c)];
-  a[b << c, b << c]; // { dg-warning "top-level comma expression in array subscript 
is deprecated" }
+  a[b << c, b << c]; // { dg-warning "top-level comma expression in array subscript is 
deprecated" "" { target c++20_down } }
+                    // { dg-error "top-level comma expression in array subscript changed 
meaning in" "" { target c++23 } .-1 }
    a[(b << c, b << c)];
  }
--- gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C.jj  2021-10-15 11:58:45.192130317 
+0200
+++ gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C     2021-11-24 12:40:37.131182956 
+0100
@@ -551,3 +551,9 @@
  #elif __cpp_if_consteval != 202106
  #  error "__cpp_if_consteval != 202106"
  #endif
+
+#ifndef __cpp_multidimensional_subscript
+#  error "__cpp_multidimensional_subscript"
+#elif __cpp_multidimensional_subscript != 202110
+#  error "__cpp_multidimensional_subscript != 202110"
+#endif
--- gcc/testsuite/g++.dg/cpp23/subscript1.C.jj  2021-11-24 12:40:37.132182942 
+0100
+++ gcc/testsuite/g++.dg/cpp23/subscript1.C     2021-11-24 12:40:37.132182942 
+0100
@@ -0,0 +1,55 @@
+// P2128R6
+// { dg-do run }
+// { dg-options "-std=c++23" }
+
+extern "C" void abort ();
+
+struct S
+{
+  constexpr S () : a {} {};
+  constexpr S (int x, int y, int z) : a {x, y, z} {};
+  constexpr int &operator[] () { return a[0]; }
+  constexpr int &operator[] (int x) { return a[x]; }
+  constexpr int &operator[] (int x, long y) { return a[x + y * 8]; }
+  int a[64];
+};
+
+struct T
+{
+  operator int () { return 42; };
+};
+
+int buf[64];
+
+struct U
+{
+  operator int * () { return buf; }
+};
+
+static_assert (S ()[1] == 0);
+static_assert (S (1, 2, 42)[2] == 42);
+static_assert (S ()[3, 4] == 0);
+static_assert (S (1, 43, 2)[1, 0] == 43);
+static_assert (S ()[] == 0);
+static_assert (S (44, 1, 2)[] == 44);
+
+int
+main ()
+{
+  S s;
+  for (int i = 0; i < 64; i++)
+    s.a[i] = 64 - i;
+  if (s[] != 64 || s[3] != 61 || s[4, 5] != 20)
+    abort ();
+  s[]++;
+  s[42]++;
+  ++s[3, 2];
+  if (s.a[0] != 65 || s.a[42] != 23 || s.a[19] != 46)
+    abort ();
+  T t;
+  U u;
+  if (&u[t] != &buf[42])
+    abort ();
+  if (&t[u] != &buf[42])
+    abort ();
+}
--- gcc/testsuite/g++.dg/cpp23/subscript2.C.jj  2021-11-24 12:40:37.132182942 
+0100
+++ gcc/testsuite/g++.dg/cpp23/subscript2.C     2021-11-24 12:40:37.132182942 
+0100
@@ -0,0 +1,51 @@
+// P2128R6
+// { dg-do compile }
+// { dg-options "-std=c++23" }
+
+struct S
+{
+  S () : a {} {};
+  int &operator[] () { return a[0]; }
+  int &operator[] (int x) { return a[x]; }
+  int &operator[] (int x, long y) { return a[x + y * 8]; }
+  int a[64];
+};
+
+struct T
+{
+  operator int () { return 42; };
+};
+
+int buf[64];
+
+struct U
+{
+  operator int * () { return buf; }
+};
+
+struct V
+{
+  V () : a {} {};
+  V (int x, int y, int z) : a {x, y, z} {};
+  int &operator[] () { return a[0]; }                              // { dg-message 
"candidate" }
+  int &operator[] (int x, long y) { return a[x + y * 8]; } // { dg-message 
"candidate" }
+  int a[64];
+};
+
+void
+foo ()
+{
+  S s;
+  T t;
+  U u;
+  V v;
+  auto &a = buf[];         // { dg-error "built-in subscript operator without 
expression list" }
+  auto &b = buf[1, 2];             // { dg-warning "top-level comma expression in 
array subscript changed meaning in" }
+  auto &c = s[1, 2, 3];            // { dg-warning "top-level comma expression in 
array subscript changed meaning in" }
+  auto &d = v[1];          // { dg-error "no match for 'operator\\\[\\\]' in 
'v\\\[1\\\]' \\\(operand types are 'V' and 'int'\\\)" }
+  auto &e = v[1, 2, 3];            // { dg-error "no match for call to 
'V::operator\\\[\\\] \\\(int, int, int\\\)'" }
+  auto &f = t[42, u];              // { dg-warning "top-level comma expression in 
array subscript changed meaning in" }
+  auto &g = u[42, t];              // { dg-warning "top-level comma expression in 
array subscript changed meaning in" }
+  auto &h = buf[42, 2.5];  // { dg-warning "top-level comma expression in array 
subscript changed meaning in" }
+                               // { dg-error "invalid types \[^\n\r]* for array 
subscript" "" { target *-*-* } .-1 }
+}
--- gcc/testsuite/g++.dg/cpp23/subscript3.C.jj  2021-11-24 12:40:37.132182942 
+0100
+++ gcc/testsuite/g++.dg/cpp23/subscript3.C     2021-11-24 12:40:37.132182942 
+0100
@@ -0,0 +1,90 @@
+// P2128R6
+// { dg-do run }
+// { dg-options "-std=c++23" }
+
+extern "C" void abort ();
+
+struct S
+{
+  constexpr S () : a {} {};
+  constexpr S (int x, int y, int z) : a {x, y, z} {};
+  constexpr int &operator[] () { return a[0]; }
+  constexpr int &operator[] (int x) { return a[x]; }
+  constexpr int &operator[] (int x, long y) { return a[x + y * 8]; }
+  int a[64];
+};
+
+struct T
+{
+  operator int () { return 42; };
+};
+
+int buf[64];
+
+struct U
+{
+  operator int * () { return buf; }
+};
+
+template <int N>
+void
+foo ()
+{
+  static_assert (S ()[1] == 0);
+  static_assert (S (1, 2, 42)[2] == 42);
+  static_assert (S ()[3, 4] == 0);
+  static_assert (S (1, 43, 2)[1, 0] == 43);
+  static_assert (S ()[] == 0);
+  static_assert (S (44, 1, 2)[] == 44);
+  S s;
+  for (int i = 0; i < 64; i++)
+    s.a[i] = 64 - i;
+  if (s[] != 64 || s[3] != 61 || s[4, 5] != 20)
+    abort ();
+  s[]++;
+  s[42]++;
+  ++s[3, 2];
+  if (s.a[0] != 65 || s.a[42] != 23 || s.a[19] != 46)
+    abort ();
+  T t;
+  U u;
+  if (&u[t] != &buf[42])
+    abort ();
+  if (&t[u] != &buf[42])
+    abort ();
+}
+
+template <typename V, typename W, typename X>
+void
+bar ()
+{
+  static_assert (V ()[1] == 0);
+  static_assert (V (1, 2, 42)[2] == 42);
+  static_assert (V ()[3, 4] == 0);
+  static_assert (V (1, 43, 2)[1, 0] == 43);
+  static_assert (V ()[] == 0);
+  static_assert (V (44, 1, 2)[] == 44);
+  V s;
+  for (int i = 0; i < 64; i++)
+    s.a[i] = 64 - i;
+  if (s[] != 64 || s[3] != 61 || s[4, 5] != 20)
+    abort ();
+  s[]++;
+  s[42]++;
+  ++s[3, 2];
+  if (s.a[0] != 65 || s.a[42] != 23 || s.a[19] != 46)
+    abort ();
+  W t;
+  X u;
+  if (&u[t] != &buf[42])
+    abort ();
+  if (&t[u] != &buf[42])
+    abort ();
+}
+
+int
+main ()
+{
+  foo <0> ();
+  bar <S, T, U> ();
+}
--- gcc/testsuite/g++.dg/cpp23/subscript4.C.jj  2021-11-24 12:40:37.132182942 
+0100
+++ gcc/testsuite/g++.dg/cpp23/subscript4.C     2021-11-24 12:40:37.132182942 
+0100
@@ -0,0 +1,44 @@
+// P2128R6
+// { dg-do run }
+// { dg-options "-std=c++23" }
+
+extern "C" void abort ();
+
+struct S
+{
+  constexpr S () : a {} {};
+  constexpr S (int x, int y, int z) : a {x, y, z} {};
+  constexpr int &operator[] () { return a[0]; }
+  constexpr int &operator[] (int x) { return a[x]; }
+  constexpr int &operator[] (int x, long y) { return a[x + y * 8]; }
+  int a[64];
+};
+int buf[26];
+
+template <class ...Ts>
+auto &
+foo (S &s, Ts... args)
+{
+  return s[args...];
+}
+
+template <typename T, class ...Ts>
+auto &
+bar (T &s, Ts... args)
+{
+  return s[args...];
+}
+
+int
+main ()
+{
+  S s;
+  if (&foo (s) != &s.a[0]
+      || &foo (s, 42) != &s.a[42]
+      || &foo (s, 5, 4) != &s.a[37]
+      || &bar (s) != &s.a[0]
+      || &bar (s, 22) != &s.a[22]
+      || &bar (s, 17, 3L) != &s.a[41]
+      || &bar (buf, 5) != &buf[5])
+    abort ();
+}
--- gcc/testsuite/g++.dg/cpp23/subscript5.C.jj  2021-11-24 12:40:37.132182942 
+0100
+++ gcc/testsuite/g++.dg/cpp23/subscript5.C     2021-11-24 12:40:37.132182942 
+0100
@@ -0,0 +1,28 @@
+// P2128R6
+// { dg-do run { target c++11 } }
+
+#include <initializer_list>
+#include <cstdlib>
+
+struct S
+{
+  S () : a {} {};
+  int &operator[] (std::initializer_list<int> l) {
+    int sum = 0;
+    for (auto x : l)
+      sum += x;
+    return a[sum];
+  }
+  int a[64];
+};
+
+int
+main ()
+{
+  S s;
+  if (&s[{0}] != &s.a[0]
+      || &s[{42}] != &s.a[42]
+      || &s[{5, 7, 9}] != &s.a[5 + 7 + 9]
+      || &s[{1, 2, 3, 4}] != &s.a[1 + 2 + 3 + 4])
+    abort ();
+}
--- gcc/testsuite/g++.dg/cpp23/subscript6.C.jj  2021-11-24 12:40:37.132182942 
+0100
+++ gcc/testsuite/g++.dg/cpp23/subscript6.C     2021-11-24 12:40:37.132182942 
+0100
@@ -0,0 +1,31 @@
+// P2128R6
+// { dg-do run }
+// { dg-options "-std=c++23" }
+
+#include <initializer_list>
+#include <cstdlib>
+
+struct S
+{
+  S () : a {} {};
+  int &operator[] (std::initializer_list<int> l, std::initializer_list<int> m) 
{
+    int sum = 0;
+    for (auto x : l)
+      sum += x;
+    for (auto x : m)
+      sum += x;
+    return a[sum];
+  }
+  int a[64];
+};
+
+int
+main ()
+{
+  S s;
+  if (&s[{0}, {3, 1, 2}] != &s.a[0 + 3 + 1 + 2]
+      || &s[{42}, {11, 1}] != &s.a[42 + 11 + 1]
+      || &s[{5, 7, 9}, {3}] != &s.a[5 + 7 + 9 + 3]
+      || &s[{1, 2, 3, 4}, {3, 5, 8}] != &s.a[1 + 2 + 3 + 4 + 3 + 5 + 8])
+    abort ();
+}


        Jakub


Reply via email to