c++, v2: Implement C++23 P2128R6 - Multidimensional subscript operator [PR102611]

Message ID 20211124133739.GN2646553@tucnak
State New
Headers show
Series
  • c++, v2: Implement C++23 P2128R6 - Multidimensional subscript operator [PR102611]
Related show

Commit Message

Petter Tomner via Gcc-patches Nov. 24, 2021, 1:37 p.m.
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?

2021-11-24  Jakub Jelinek  <jakub@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.



	Jakub

Comments

Petter Tomner via Gcc-patches Nov. 25, 2021, 2:43 a.m. | #1
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  <jakub@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

>

Patch

--- 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 ();
+}