public inbox for gcc-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc r12-5510] c++: Implement C++23 P2128R6 - Multidimensional subscript operator [PR102611]
@ 2021-11-25  7:38 Jakub Jelinek
  0 siblings, 0 replies; only message in thread
From: Jakub Jelinek @ 2021-11-25  7:38 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:b38c9cf6d570f6c4c1109e00c8b81d82d0f24df3

commit r12-5510-gb38c9cf6d570f6c4c1109e00c8b81d82d0f24df3
Author: Jakub Jelinek <jakub@redhat.com>
Date:   Thu Nov 25 08:36:20 2021 +0100

    c++: Implement C++23 P2128R6 - Multidimensional subscript operator [PR102611]
    
    The following patch implements the C++23 Multidimensional subscript operator
    P2128R6 paper.
    As C++20 and older only allow a single expression in between []s (albeit
    for C++20 with a deprecation warning if it is a comma expression) and even
    in C++23 and for the coming years I think the vast majority of subscript
    expressions will still have a single expression and even in C++23 it is
    quite special, as e.g. the builtin operator requires exactly one
    assignment expression, the patch attempts to optimize for that case and
    if possible not to slow down that common case (or use more memory for it).
    So, already during parsing it differentiates between that (uses a single
    index_exp tree in that case) and the new cases (zero or two+ expressions
    in the list), for which it sets index_exp to NULL_TREE and uses a
    releasing_vec instead similarly to how e.g. finish_call_expr uses it.
    In call.c it introduces new functions build_op_subscript{,_1} which are
    something in between build_new_op{,_1} and build_op_call{,_1}.
    The former requires fixed number of arguments (and the patch still uses
    it for the common case of subscript with exactly one index expression),
    the latter handles variable number of arguments but is too CALL_EXPR specific
    and handles various cases that are unnecessary for the subscript.
    Right now the subscript for 0 or 2+ expressions doesn't need to deal with
    builtin candidates and so is quite simple.
    
    As discussed in the paper, for backwards compatibility, if for 2+ index
    expressions build_op_subscript fails (called with tf_none) and the
    expressions together form a valid comma expression (again checked with
    tf_none), it is used that C++20-ish way with a pedwarn about it, but if
    even that fails, build_op_subscript is called again with standard complain
    flags to diagnose it in the new way.  And similarly for the builtin case.
    
    The -Wcomma-subscript warning used to be enabled by default unless
    -Wno-deprecated.  Since the C/C++98..20 behavior is no longer deprecated,
    but ill-formed or changed meaning, it is now for C++23 enabled by
    default regardless of -Wno-deprecated and controls the pedwarn (but not the
    errors emitted if something wasn't valid before and isn't valid in C++23
    either).
    
    2021-11-25  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.

Diff:
---
 gcc/c-family/c-cppbuiltin.c             |   1 +
 gcc/c-family/c-opts.c                   |   3 +-
 gcc/cp/call.c                           | 115 ++++++++++++++++++++++++-
 gcc/cp/cp-tree.h                        |  12 ++-
 gcc/cp/decl.c                           |   2 +
 gcc/cp/decl2.c                          | 125 +++++++++++++++++++++++----
 gcc/cp/parser.c                         | 147 +++++++++++++++++++++++---------
 gcc/cp/pt.c                             | 101 +++++++++++++++-------
 gcc/cp/semantics.c                      |   3 +-
 gcc/cp/tree.c                           |  31 ++++++-
 gcc/doc/invoke.texi                     |  16 +++-
 gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C |   6 ++
 gcc/testsuite/g++.dg/cpp23/subscript1.C |  55 ++++++++++++
 gcc/testsuite/g++.dg/cpp23/subscript2.C |  51 +++++++++++
 gcc/testsuite/g++.dg/cpp23/subscript3.C |  90 +++++++++++++++++++
 gcc/testsuite/g++.dg/cpp23/subscript4.C |  44 ++++++++++
 gcc/testsuite/g++.dg/cpp23/subscript5.C |  28 ++++++
 gcc/testsuite/g++.dg/cpp23/subscript6.C |  31 +++++++
 gcc/testsuite/g++.dg/cpp2a/comma1.C     |  15 ++--
 gcc/testsuite/g++.dg/cpp2a/comma3.C     |  15 ++--
 gcc/testsuite/g++.dg/cpp2a/comma4.C     |   5 ++
 gcc/testsuite/g++.dg/cpp2a/comma5.C     |  18 ++--
 22 files changed, 796 insertions(+), 118 deletions(-)

diff --git a/gcc/c-family/c-cppbuiltin.c b/gcc/c-family/c-cppbuiltin.c
index eb34d5af004..feb7e611de6 100644
--- a/gcc/c-family/c-cppbuiltin.c
+++ b/gcc/c-family/c-cppbuiltin.c
@@ -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)
         {
diff --git a/gcc/c-family/c-opts.c b/gcc/c-family/c-opts.c
index 62f58295c11..2030eb1a4cd 100644
--- a/gcc/c-family/c-opts.c
+++ b/gcc/c-family/c-opts.c
@@ -946,7 +946,8 @@ c_common_post_options (const char **pfilename)
   /* -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,
diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index 27f4d9e7787..28bd8e0c260 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -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, enum tree_code code, int flags,
   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 *cand, int flags, tsubst_flags_t complain)
   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)
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 2037082b0c7..477996911ff 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -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_location_t &loc, enum tree_code code,
 }
 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_t, enum tree_code,
 						 ...);
 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);
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index 588094b1db6..56f80775ca0 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -15140,6 +15140,8 @@ grok_op_properties (tree decl, bool complain)
     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")
diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c
index 32d3fe3636d..99f5dc784b7 100644
--- a/gcc/cp/decl2.c
+++ b/gcc/cp/decl2.c
@@ -363,16 +363,20 @@ grokclassfn (tree ctype, tree function, enum overload_flags flags)
 }
 
 /* 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 array_expr, tree index_exp,
   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 array_expr, tree index_exp,
   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)))
-    {
-      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 (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 ()))))
+    {
+      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 array_expr, tree index_exp,
       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 array_expr, tree index_exp,
   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;
 }
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 7a6a30208ef..0bd58525726 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -7898,11 +7898,62 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
   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 (cp_parser *parser,
 					  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 (cp_parser *parser,
     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 (cp_parser *parser,
 
   /* 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 (cp_parser* parser,
 	  }
 	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 *parser)
 
 	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:
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 288625e3e93..49e5745410e 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -19654,6 +19654,49 @@ maybe_fold_fn_template_args (tree fn, tsubst_flags_t complain)
   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.  */
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index 79b8162ef1e..cd1956497f8 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -5394,7 +5394,8 @@ handle_omp_array_sections_1 (tree c, tree t, vec<tree> &types,
 		      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;
 }
 
diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
index 7050a53abc2..7c58e23db3c 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -3671,7 +3671,7 @@ build_min_non_dep_op_overload (enum tree_code op,
 	}
     }
   else
-   gcc_unreachable ();
+    gcc_unreachable ();
 
   va_end (p);
   call = build_min_non_dep_call_vec (non_dep, fn, args);
@@ -3685,6 +3685,35 @@ build_min_non_dep_op_overload (enum tree_code op,
   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);
+
+  return call;
+}
+
 /* Return a new tree vec copied from VEC, with ELT inserted at index IDX.  */
 
 vec<tree, va_gc> *
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 36fe96b441b..d0ac59768b9 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -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
diff --git a/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C b/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C
index 8bb3bf1f00f..923e6bcf65e 100644
--- a/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C
+++ b/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C
@@ -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
diff --git a/gcc/testsuite/g++.dg/cpp23/subscript1.C b/gcc/testsuite/g++.dg/cpp23/subscript1.C
new file mode 100644
index 00000000000..a96c93eef82
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/subscript1.C
@@ -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 ();
+}
diff --git a/gcc/testsuite/g++.dg/cpp23/subscript2.C b/gcc/testsuite/g++.dg/cpp23/subscript2.C
new file mode 100644
index 00000000000..f8ec87d88ef
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/subscript2.C
@@ -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 }
+}
diff --git a/gcc/testsuite/g++.dg/cpp23/subscript3.C b/gcc/testsuite/g++.dg/cpp23/subscript3.C
new file mode 100644
index 00000000000..2d735e4e0e0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/subscript3.C
@@ -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> ();
+}
diff --git a/gcc/testsuite/g++.dg/cpp23/subscript4.C b/gcc/testsuite/g++.dg/cpp23/subscript4.C
new file mode 100644
index 00000000000..085095d9dee
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/subscript4.C
@@ -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 ();
+}
diff --git a/gcc/testsuite/g++.dg/cpp23/subscript5.C b/gcc/testsuite/g++.dg/cpp23/subscript5.C
new file mode 100644
index 00000000000..b36bc774ec4
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/subscript5.C
@@ -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 ();
+}
diff --git a/gcc/testsuite/g++.dg/cpp23/subscript6.C b/gcc/testsuite/g++.dg/cpp23/subscript6.C
new file mode 100644
index 00000000000..72c7aef42af
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/subscript6.C
@@ -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 ();
+}
diff --git a/gcc/testsuite/g++.dg/cpp2a/comma1.C b/gcc/testsuite/g++.dg/cpp2a/comma1.C
index d9c140db23a..1799c187ad5 100644
--- a/gcc/testsuite/g++.dg/cpp2a/comma1.C
+++ b/gcc/testsuite/g++.dg/cpp2a/comma1.C
@@ -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{})];
 }
diff --git a/gcc/testsuite/g++.dg/cpp2a/comma3.C b/gcc/testsuite/g++.dg/cpp2a/comma3.C
index c39dd4b483a..daf53596049 100644
--- a/gcc/testsuite/g++.dg/cpp2a/comma3.C
+++ b/gcc/testsuite/g++.dg/cpp2a/comma3.C
@@ -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{})];
 }
diff --git a/gcc/testsuite/g++.dg/cpp2a/comma4.C b/gcc/testsuite/g++.dg/cpp2a/comma4.C
index 0d149c7a9af..11aff99c5db 100644
--- a/gcc/testsuite/g++.dg/cpp2a/comma4.C
+++ b/gcc/testsuite/g++.dg/cpp2a/comma4.C
@@ -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{})];
 }
diff --git a/gcc/testsuite/g++.dg/cpp2a/comma5.C b/gcc/testsuite/g++.dg/cpp2a/comma5.C
index acf5d43ad5d..8bf94d14ac9 100644
--- a/gcc/testsuite/g++.dg/cpp2a/comma5.C
+++ b/gcc/testsuite/g++.dg/cpp2a/comma5.C
@@ -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)]; 
 }


^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2021-11-25  7:38 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-11-25  7:38 [gcc r12-5510] c++: Implement C++23 P2128R6 - Multidimensional subscript operator [PR102611] Jakub Jelinek

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).