From patchwork Wed Nov 24 13:37:39 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jakub Jelinek X-Patchwork-Id: 48063 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 039D23858425 for ; Wed, 24 Nov 2021 13:38:34 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 039D23858425 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1637761114; bh=ev6s1hLCQzZQ8NERd32Nsnr7/q1ezLDMxwU3GV8AT1Y=; h=Date:To:Subject:References:In-Reply-To:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc: From; b=bZwc8TZ9uSUcbpfGIFWuFCOVIf8ab/uzNxUWyyeFP01nAGGwzXHLsKB0++xQc2UUr fWa/m52k33KDqNAS1eFQcYS/d6MhTIp5bMjjCcsDTzOWujTe1U2dFbxL/DUzHa5Egh ORkl5InBfJrz2gxkIzBUvI/kHUm+S1iapUbbirBo= X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTPS id 6AD9E3858C27 for ; Wed, 24 Nov 2021 13:37:55 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 6AD9E3858C27 Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-38-OzLw0gSzOj-3ETYPNmbPHw-1; Wed, 24 Nov 2021 08:37:51 -0500 X-MC-Unique: OzLw0gSzOj-3ETYPNmbPHw-1 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.phx2.redhat.com [10.5.11.23]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id C5F02800E00; Wed, 24 Nov 2021 13:37:50 +0000 (UTC) Received: from tucnak.zalov.cz (unknown [10.39.192.23]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 046DC19C46; Wed, 24 Nov 2021 13:37:43 +0000 (UTC) Received: from tucnak.zalov.cz (localhost [127.0.0.1]) by tucnak.zalov.cz (8.16.1/8.16.1) with ESMTPS id 1AODbe0X4001533 (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 bits=256 verify=NOT); Wed, 24 Nov 2021 14:37:40 +0100 Received: (from jakub@localhost) by tucnak.zalov.cz (8.16.1/8.16.1/Submit) id 1AODbdCS4001532; Wed, 24 Nov 2021 14:37:39 +0100 Date: Wed, 24 Nov 2021 14:37:39 +0100 To: Jason Merrill Subject: [PATCH] c++, v2: Implement C++23 P2128R6 - Multidimensional subscript operator [PR102611] Message-ID: <20211124133739.GN2646553@tucnak> References: <20211014082629.GR304296@tucnak> MIME-Version: 1.0 In-Reply-To: X-Scanned-By: MIMEDefang 2.84 on 10.5.11.23 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Disposition: inline X-Spam-Status: No, score=-5.7 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, RCVD_IN_DNSWL_LOW, RCVD_IN_MSPIKE_H2, SPF_HELO_NONE, SPF_NONE, TXREP autolearn=ham autolearn_force=no version=3.4.4 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Jakub Jelinek via Gcc-patches From: Jakub Jelinek Reply-To: Jakub Jelinek Cc: Jonathan Wakely , gcc-patches@gcc.gnu.org Errors-To: gcc-patches-bounces+patchwork=sourceware.org@gcc.gnu.org Sender: "Gcc-patches" 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 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 ** 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) : 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. : 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 --- 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 **, tsubst_flags_t); +extern tree build_op_subscript (const op_location_t &, tree, + vec **, 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 **, 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 *); extern tree build_min_nt_call_vec (tree, vec *); extern tree build_min_non_dep_call_vec (tree, tree, vec *); extern vec* vec_copy_and_insert (vec*, 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 **index_exp_list, tsubst_flags_t complain) { tree type; tree expr; tree orig_array_expr = array_expr; tree orig_index_exp = index_exp; + vec *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 *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 **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 (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(1, 2)]; - a[foo(1, 2), foo(3, 4)]; // { dg-warning "24:top-level comma expression in array subscript is deprecated" } + a[foo(1, 2), foo(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 +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 +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 (); +} --- 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 +auto & +foo (S &s, Ts... args) +{ + return s[args...]; +} + +template +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 +#include + +struct S +{ + S () : a {} {}; + int &operator[] (std::initializer_list 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 +#include + +struct S +{ + S () : a {} {}; + int &operator[] (std::initializer_list l, std::initializer_list 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 (); +}