diff options
author | Jakub Jelinek <jakub@redhat.com> | 2021-11-25 08:36:20 +0100 |
---|---|---|
committer | Jakub Jelinek <jakub@redhat.com> | 2021-11-25 08:36:20 +0100 |
commit | b38c9cf6d570f6c4c1109e00c8b81d82d0f24df3 (patch) | |
tree | 78aa38700bd9118938075f5d215d5a89cec32ca5 /gcc/cp/parser.c | |
parent | f88e50780137503365192011b261498b06c75930 (diff) | |
download | gcc-b38c9cf6d570f6c4c1109e00c8b81d82d0f24df3.zip gcc-b38c9cf6d570f6c4c1109e00c8b81d82d0f24df3.tar.gz gcc-b38c9cf6d570f6c4c1109e00c8b81d82d0f24df3.tar.bz2 |
c++: Implement C++23 P2128R6 - Multidimensional subscript operator [PR102611]
The following patch implements the C++23 Multidimensional subscript operator
P2128R6 paper.
As C++20 and older only allow a single expression in between []s (albeit
for C++20 with a deprecation warning if it is a comma expression) and even
in C++23 and for the coming years I think the vast majority of subscript
expressions will still have a single expression and even in C++23 it is
quite special, as e.g. the builtin operator requires exactly one
assignment expression, the patch attempts to optimize for that case and
if possible not to slow down that common case (or use more memory for it).
So, already during parsing it differentiates between that (uses a single
index_exp tree in that case) and the new cases (zero or two+ expressions
in the list), for which it sets index_exp to NULL_TREE and uses a
releasing_vec instead similarly to how e.g. finish_call_expr uses it.
In call.c it introduces new functions build_op_subscript{,_1} which are
something in between build_new_op{,_1} and build_op_call{,_1}.
The former requires fixed number of arguments (and the patch still uses
it for the common case of subscript with exactly one index expression),
the latter handles variable number of arguments but is too CALL_EXPR specific
and handles various cases that are unnecessary for the subscript.
Right now the subscript for 0 or 2+ expressions doesn't need to deal with
builtin candidates and so is quite simple.
As discussed in the paper, for backwards compatibility, if for 2+ index
expressions build_op_subscript fails (called with tf_none) and the
expressions together form a valid comma expression (again checked with
tf_none), it is used that C++20-ish way with a pedwarn about it, but if
even that fails, build_op_subscript is called again with standard complain
flags to diagnose it in the new way. And similarly for the builtin case.
The -Wcomma-subscript warning used to be enabled by default unless
-Wno-deprecated. Since the C/C++98..20 behavior is no longer deprecated,
but ill-formed or changed meaning, it is now for C++23 enabled by
default regardless of -Wno-deprecated and controls the pedwarn (but not the
errors emitted if something wasn't valid before and isn't valid in C++23
either).
2021-11-25 Jakub Jelinek <jakub@redhat.com>
PR c++/102611
gcc/
* doc/invoke.texi (-Wcomma-subscript): Document that for
-std=c++20 the option isn't enabled by default with -Wno-deprecated
but for -std=c++23 it is.
gcc/c-family/
* c-opts.c (c_common_post_options): Enable -Wcomma-subscript by
default for C++23 regardless of warn_deprecated.
* c-cppbuiltin.c (c_cpp_builtins): Predefine
__cpp_multidimensional_subscript=202110L for C++23.
gcc/cp/
* cp-tree.h (build_op_subscript): Implement P2128R6
- Multidimensional subscript operator. Declare.
(class releasing_vec): Add release method.
(grok_array_decl): Remove bool argument, add vec<tree, va_gc> **
and tsubst_flags_t arguments.
(build_min_non_dep_op_overload): Declare another overload.
* parser.c (cp_parser_parenthesized_expression_list_elt): New function.
(cp_parser_postfix_open_square_expression): Mention C++23 syntax in
function comment. For C++23 parse zero or more than one initializer
clauses in expression list, adjust grok_array_decl caller.
(cp_parser_parenthesized_expression_list): Use
cp_parser_parenthesized_expression_list_elt.
(cp_parser_builtin_offsetof): Adjust grok_array_decl caller.
* decl.c (grok_op_properties): For C++23 don't check number
of arguments of operator[].
* decl2.c (grok_array_decl): Remove decltype_p argument, add
index_exp_list and complain arguments. If index_exp is NULL,
handle *index_exp_list as the subscript expression list.
* tree.c (build_min_non_dep_op_overload): New overload.
* call.c (add_operator_candidates, build_over_call): Adjust comments
for removal of build_new_op_1.
(build_op_subscript): New function.
* pt.c (tsubst_copy_and_build_call_args): New function.
(tsubst_copy_and_build) <case ARRAY_REF>: If second
operand is magic CALL_EXPR with ovl_op_identifier (ARRAY_REF)
as CALL_EXPR_FN, tsubst CALL_EXPR arguments including expanding
pack expressions in it and call grok_array_decl instead of
build_x_array_ref.
<case CALL_EXPR>: Use tsubst_copy_and_build_call_args.
* semantics.c (handle_omp_array_sections_1): Adjust grok_array_decl
caller.
gcc/testsuite/
* g++.dg/cpp2a/comma1.C: Expect different diagnostics for C++23.
* g++.dg/cpp2a/comma3.C: Likewise.
* g++.dg/cpp2a/comma4.C: Expect diagnostics for C++23.
* g++.dg/cpp2a/comma5.C: Expect different diagnostics for C++23.
* g++.dg/cpp23/feat-cxx2b.C: Test __cpp_multidimensional_subscript
predefined macro.
* g++.dg/cpp23/subscript1.C: New test.
* g++.dg/cpp23/subscript2.C: New test.
* g++.dg/cpp23/subscript3.C: New test.
* g++.dg/cpp23/subscript4.C: New test.
* g++.dg/cpp23/subscript5.C: New test.
* g++.dg/cpp23/subscript6.C: New test.
Diffstat (limited to 'gcc/cp/parser.c')
-rw-r--r-- | gcc/cp/parser.c | 147 |
1 files changed, 105 insertions, 42 deletions
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c index 7a6a302..0bd5852 100644 --- a/gcc/cp/parser.c +++ b/gcc/cp/parser.c @@ -7898,11 +7898,62 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p, return error_mark_node; } +/* Helper function for cp_parser_parenthesized_expression_list and + cp_parser_postfix_open_square_expression. Parse a single element + of parenthesized expression list. */ + +static cp_expr +cp_parser_parenthesized_expression_list_elt (cp_parser *parser, bool cast_p, + bool allow_expansion_p, + bool fold_expr_p, + bool *non_constant_p) +{ + cp_expr expr (NULL_TREE); + bool expr_non_constant_p; + + /* Parse the next assignment-expression. */ + if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE)) + { + /* A braced-init-list. */ + cp_lexer_set_source_position (parser->lexer); + maybe_warn_cpp0x (CPP0X_INITIALIZER_LISTS); + expr = cp_parser_braced_list (parser, &expr_non_constant_p); + if (non_constant_p && expr_non_constant_p) + *non_constant_p = true; + } + else if (non_constant_p) + { + expr = cp_parser_constant_expression (parser, + /*allow_non_constant_p=*/true, + &expr_non_constant_p); + if (expr_non_constant_p) + *non_constant_p = true; + } + else + expr = cp_parser_assignment_expression (parser, /*pidk=*/NULL, cast_p); + + if (fold_expr_p) + expr = instantiate_non_dependent_expr (expr); + + /* If we have an ellipsis, then this is an expression expansion. */ + if (allow_expansion_p + && cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS)) + { + /* Consume the `...'. */ + cp_lexer_consume_token (parser->lexer); + + /* Build the argument pack. */ + expr = make_pack_expansion (expr); + } + return expr; +} + /* A subroutine of cp_parser_postfix_expression that also gets hijacked by cp_parser_builtin_offsetof. We're looking for postfix-expression [ expression ] postfix-expression [ braced-init-list ] (C++11) + postfix-expression [ expression-list[opt] ] (C++23) FOR_OFFSETOF is set if we're being called in that context, which changes how we deal with integer constant expressions. */ @@ -7914,6 +7965,7 @@ cp_parser_postfix_open_square_expression (cp_parser *parser, bool decltype_p) { tree index = NULL_TREE; + releasing_vec expression_list = NULL; location_t loc = cp_lexer_peek_token (parser->lexer)->location; bool saved_greater_than_is_operator_p; @@ -7935,7 +7987,49 @@ cp_parser_postfix_open_square_expression (cp_parser *parser, index = cp_parser_constant_expression (parser); else { - if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE)) + if (cxx_dialect >= cxx23 + && cp_lexer_next_token_is (parser->lexer, CPP_CLOSE_SQUARE)) + *&expression_list = make_tree_vector (); + else if (cxx_dialect >= cxx23) + { + while (true) + { + cp_expr expr + = cp_parser_parenthesized_expression_list_elt (parser, + /*cast_p=*/ + false, + /*allow_exp_p=*/ + true, + /*fold_expr_p=*/ + false, + /*non_cst_p=*/ + NULL); + + if (expr == error_mark_node) + index = error_mark_node; + else if (expression_list.get () == NULL + && !PACK_EXPANSION_P (expr.get_value ())) + index = expr.get_value (); + else + vec_safe_push (expression_list, expr.get_value ()); + + /* If the next token isn't a `,', then we are done. */ + if (cp_lexer_next_token_is_not (parser->lexer, CPP_COMMA)) + break; + + if (expression_list.get () == NULL && index != error_mark_node) + { + *&expression_list = make_tree_vector_single (index); + index = NULL_TREE; + } + + /* Otherwise, consume the `,' and keep going. */ + cp_lexer_consume_token (parser->lexer); + } + if (expression_list.get () && index == error_mark_node) + expression_list.release (); + } + else if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE)) { bool expr_nonconst_p; cp_lexer_set_source_position (parser->lexer); @@ -7955,7 +8049,9 @@ cp_parser_postfix_open_square_expression (cp_parser *parser, /* Build the ARRAY_REF. */ postfix_expression = grok_array_decl (loc, postfix_expression, - index, decltype_p); + index, &expression_list, + tf_warning_or_error + | (decltype_p ? tf_decltype : 0)); /* When not doing offsetof, array references are not permitted in constant-expressions. */ @@ -8315,44 +8411,11 @@ cp_parser_parenthesized_expression_list (cp_parser* parser, } else { - bool expr_non_constant_p; - - /* Parse the next assignment-expression. */ - if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE)) - { - /* A braced-init-list. */ - cp_lexer_set_source_position (parser->lexer); - maybe_warn_cpp0x (CPP0X_INITIALIZER_LISTS); - expr = cp_parser_braced_list (parser, &expr_non_constant_p); - if (non_constant_p && expr_non_constant_p) - *non_constant_p = true; - } - else if (non_constant_p) - { - expr = (cp_parser_constant_expression - (parser, /*allow_non_constant_p=*/true, - &expr_non_constant_p)); - if (expr_non_constant_p) - *non_constant_p = true; - } - else - expr = cp_parser_assignment_expression (parser, /*pidk=*/NULL, - cast_p); - - if (fold_expr_p) - expr = instantiate_non_dependent_expr (expr); - - /* If we have an ellipsis, then this is an expression - expansion. */ - if (allow_expansion_p - && cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS)) - { - /* Consume the `...'. */ - cp_lexer_consume_token (parser->lexer); - - /* Build the argument pack. */ - expr = make_pack_expansion (expr); - } + expr + = cp_parser_parenthesized_expression_list_elt (parser, cast_p, + allow_expansion_p, + fold_expr_p, + non_constant_p); if (wrap_locations_p) expr.maybe_add_location_wrapper (); @@ -10625,8 +10688,8 @@ cp_parser_builtin_offsetof (cp_parser *parser) case CPP_DEREF: /* offsetof-member-designator "->" identifier */ - expr = grok_array_decl (token->location, expr, - integer_zero_node, false); + expr = grok_array_decl (token->location, expr, integer_zero_node, + NULL, tf_warning_or_error); /* FALLTHRU */ case CPP_DOT: |