Message ID | 20211124133739.GN2646553@tucnak |
---|---|
State | New |
Headers | show |
Series | c++, v2: Implement C++23 P2128R6 - Multidimensional subscript operator [PR102611] | expand |
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 >
--- 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 (); +}