c++, v2: Implement C++23 P2128R6 - Multidimensional subscript operator [PR102611]
Commit Message
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
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
>
@@ -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
@@ -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,
@@ -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)
{
@@ -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);
@@ -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:
@@ -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")
@@ -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;
}
@@ -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);
@@ -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)
@@ -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. */
@@ -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;
}
@@ -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{})];
}
@@ -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{})];
}
@@ -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{})];
}
@@ -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)];
}
@@ -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
@@ -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 ();
+}
@@ -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 }
+}
@@ -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> ();
+}
@@ -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 ();
+}
@@ -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 ();
+}
@@ -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 ();
+}