This patch implements P1161R3, Deprecate uses of the comma operator in subscripting expressions: <http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1161r3.html> which made its way to C++20. New [depr.comma.subscript] shows:
void f(int *a, int b, int c) { a[b,c]; // deprecated a[(b,c)]; // OK } I've added a new option, -Wcomma-subscript, so that this warning can be selectively turned off in c++2a, where it is enabled by default, and turned on in lower standards, to check if your codebase has any occurrences of it, without needing to change the -std=. As for implementation, I've extended cp_parser_skip_to_closing_square_bracket_1 so that we can check for a non-nested token inside [ ]. Bootstrapped/regtested on x86_64-linux, ok for trunk? 2019-08-03 Marek Polacek <pola...@redhat.com> PR c++/91338 - Implement P1161R3: Deprecate a[b,c]. * c-opts.c (c_common_post_options): Enable -Wcomma-subscript by default for C++2a. * c.opt (Wcomma-subscript): New warning. * parser.c (cp_parser_postfix_open_square_expression): Warn about uses of a comma operator within a subscripting expression. (cp_parser_skip_to_closing_square_bracket_1): New function, made out of... (cp_parser_skip_to_closing_square_bracket): ...this. * doc/invoke.texi: Document -Wcomma-subscript. * g++.dg/cpp2a/comma1.C: New test. * g++.dg/cpp2a/comma2.C: New test. * g++.dg/cpp2a/comma3.C: New test. diff --git gcc/c-family/c-opts.c gcc/c-family/c-opts.c index e97bbdf5c6f..e43172598f3 100644 --- gcc/c-family/c-opts.c +++ gcc/c-family/c-opts.c @@ -916,6 +916,10 @@ c_common_post_options (const char **pfilename) if (!global_options_set.x_warn_register) warn_register = cxx_dialect >= cxx17; + /* -Wcomma-subscript is enabled by default in C++20. */ + if (!global_options_set.x_warn_comma_subscript) + warn_comma_subscript = cxx_dialect >= cxx2a; + /* Declone C++ 'structors if -Os. */ if (flag_declone_ctor_dtor == -1) flag_declone_ctor_dtor = optimize_size; diff --git gcc/c-family/c.opt gcc/c-family/c.opt index 4c8b0026000..257cadfa5f1 100644 --- gcc/c-family/c.opt +++ gcc/c-family/c.opt @@ -428,6 +428,10 @@ Wclobbered C ObjC C++ ObjC++ Var(warn_clobbered) Warning EnabledBy(Wextra) Warn about variables that might be changed by \"longjmp\" or \"vfork\". +Wcomma-subscript +C++ ObjC++ Var(warn_comma_subscript) Warning +Warn about uses of a comma operator within a subscripting expression. + Wcomment C ObjC C++ ObjC++ CPP(warn_comments) CppReason(CPP_W_COMMENTS) Var(cpp_warn_comment) Init(0) Warning LangEnabledBy(C ObjC C++ ObjC++,Wall) Warn about possibly nested block comments, and C++ comments spanning more than one physical line. diff --git gcc/cp/parser.c gcc/cp/parser.c index ebeffdb775f..1a5ae147b84 100644 --- gcc/cp/parser.c +++ gcc/cp/parser.c @@ -2669,6 +2669,8 @@ static bool cp_parser_init_statement_p (cp_parser *); static bool cp_parser_skip_to_closing_square_bracket (cp_parser *); +static int cp_parser_skip_to_closing_square_bracket_1 + (cp_parser *, enum cpp_ttype); /* Concept-related syntactic transformations */ @@ -7522,7 +7524,33 @@ cp_parser_postfix_open_square_expression (cp_parser *parser, index = cp_parser_braced_list (parser, &expr_nonconst_p); } else - index = cp_parser_expression (parser); + { + /* [depr.comma.subscript]: A comma expression appearing as + the expr-or-braced-init-list of a subscripting expression + is deprecated. A parenthesized comma expression is not + deprecated. */ + if (warn_comma_subscript) + { + /* Save tokens so that we can put them back. */ + cp_lexer_save_tokens (parser->lexer); + + /* Look for ',' that is not nested in () or {}. */ + if (cp_parser_skip_to_closing_square_bracket_1 (parser, + CPP_COMMA) == -1) + { + auto_diagnostic_group d; + warning_at (cp_lexer_peek_token (parser->lexer)->location, + OPT_Wcomma_subscript, + "top-level comma expression in array subscript " + "is deprecated"); + } + + /* Roll back the tokens we skipped. */ + cp_lexer_rollback_tokens (parser->lexer); + } + + index = cp_parser_expression (parser); + } } parser->greater_than_is_operator_p = saved_greater_than_is_operator_p; @@ -22857,16 +22885,25 @@ cp_parser_braced_list (cp_parser* parser, bool* non_constant_p) } /* Consume tokens up to, and including, the next non-nested closing `]'. - Returns true iff we found a closing `]'. */ + Returns 1 iff we found a closing `]'. Returns -1 if OR_TTYPE is not + CPP_EOF and we found an unnested token of that type. */ -static bool -cp_parser_skip_to_closing_square_bracket (cp_parser *parser) +static int +cp_parser_skip_to_closing_square_bracket_1 (cp_parser *parser, + enum cpp_ttype or_ttype) { unsigned square_depth = 0; + unsigned paren_depth = 0; + unsigned brace_depth = 0; while (true) { - cp_token * token = cp_lexer_peek_token (parser->lexer); + cp_token *token = cp_lexer_peek_token (parser->lexer); + + /* Have we found what we're looking for before the closing square? */ + if (token->type == or_ttype && or_ttype != CPP_EOF + && brace_depth == 0 && paren_depth == 0 && square_depth == 0) + return -1; switch (token->type) { @@ -22876,20 +22913,38 @@ cp_parser_skip_to_closing_square_bracket (cp_parser *parser) /* FALLTHRU */ case CPP_EOF: /* If we've run out of tokens, then there is no closing `]'. */ - return false; + return 0; case CPP_OPEN_SQUARE: ++square_depth; break; case CPP_CLOSE_SQUARE: - if (!square_depth--) + if (square_depth-- == 0) { cp_lexer_consume_token (parser->lexer); - return true; + return 1; } break; + case CPP_OPEN_BRACE: + ++brace_depth; + break; + + case CPP_CLOSE_BRACE: + if (brace_depth-- == 0) + return 0; + break; + + case CPP_OPEN_PAREN: + ++paren_depth; + break; + + case CPP_CLOSE_PAREN: + if (paren_depth-- == 0) + return 0; + break; + default: break; } @@ -22899,6 +22954,15 @@ cp_parser_skip_to_closing_square_bracket (cp_parser *parser) } } +/* Consume tokens up to, and including, the next non-nested closing `]'. + Returns true iff we found a closing `]'. */ + +static bool +cp_parser_skip_to_closing_square_bracket (cp_parser *parser) +{ + return cp_parser_skip_to_closing_square_bracket_1 (parser, CPP_EOF) == 1; +} + /* Return true if we are looking at an array-designator, false otherwise. */ static bool diff --git gcc/doc/invoke.texi gcc/doc/invoke.texi index 7b3c77b8033..01aab60f895 100644 --- gcc/doc/invoke.texi +++ gcc/doc/invoke.texi @@ -230,7 +230,8 @@ in the following sections. -fvisibility-inlines-hidden @gol -fvisibility-ms-compat @gol -fext-numeric-literals @gol --Wabi=@var{n} -Wabi-tag -Wconversion-null -Wctor-dtor-privacy @gol +-Wabi=@var{n} -Wabi-tag -Wcomma-subscript -Wconversion-null @gol +-Wctor-dtor-privacy @gol -Wdelete-non-virtual-dtor -Wdeprecated-copy -Wdeprecated-copy-dtor @gol -Wliteral-suffix @gol -Wmultiple-inheritance -Wno-init-list-lifetime @gol @@ -3037,6 +3038,24 @@ Warn when a type with an ABI tag is used in a context that does not have that ABI tag. See @ref{C++ Attributes} for more information about ABI tags. +@item -Wcomma-subscript @r{(C++ and Objective-C++ only)} +@opindex Wcomma-subscript +@opindex Wno-comma-subscript +Warn about uses of a comma expression within a subscripting expression. +This usage was deprecated in C++2a. 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)]; // OK +@} +@end group +@end smallexample + +Enabled by default with @option{-std=c++2a}. + @item -Wctor-dtor-privacy @r{(C++ and Objective-C++ only)} @opindex Wctor-dtor-privacy @opindex Wno-ctor-dtor-privacy diff --git gcc/testsuite/g++.dg/cpp2a/comma1.C gcc/testsuite/g++.dg/cpp2a/comma1.C new file mode 100644 index 00000000000..5de48b4cdb9 --- /dev/null +++ gcc/testsuite/g++.dg/cpp2a/comma1.C @@ -0,0 +1,26 @@ +// PR c++/91338 - P1161R3: Deprecate a[b,c]. +// { dg-do compile { target c++11 } } + +struct S { + int operator,(int) { return 42; } +}; + +void +fn (int *a, int b, int c) +{ + a[b,c]; // { dg-warning "top-level comma expression in array subscript is deprecated" "" { target c++2a } } + a[(b,c)]; + + a[(void) b, c]; // { dg-warning "top-level comma expression in array subscript is deprecated" "" { target c++2a } } + 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++2a } } + a[((void) b, (void) c, (void) b, b)]; + + a[S(), 10]; // { dg-warning "top-level comma expression in array subscript is deprecated" "" { target c++2a } } + 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++2a } } + a[(int{(1,2)}, int{})]; +} diff --git gcc/testsuite/g++.dg/cpp2a/comma2.C gcc/testsuite/g++.dg/cpp2a/comma2.C new file mode 100644 index 00000000000..383079ce4f8 --- /dev/null +++ gcc/testsuite/g++.dg/cpp2a/comma2.C @@ -0,0 +1,27 @@ +// PR c++/91338 - P1161R3: Deprecate a[b,c]. +// { dg-do compile { target c++2a } } +// { dg-options "-Wno-comma-subscript" } + +struct S { + int operator,(int) { return 42; } +}; + +void +fn (int *a, int b, int c) +{ + a[b,c]; // { dg-bogus "top-level comma expression in array subscript is deprecated" } + a[(b,c)]; + + a[(void) b, c]; // { dg-bogus "top-level comma expression in array subscript is deprecated" } + a[((void) b, c)]; + + a[(void) b, (void) c, (void) b, b]; // { dg-bogus "top-level comma expression in array subscript is deprecated" } + a[((void) b, (void) c, (void) b, b)]; + + a[S(), 10]; // { dg-bogus "top-level comma expression in array subscript is deprecated" } + a[(S(), 10)]; + + a[int{(1,2)}]; + a[int{(1,2)}, int{}]; // { dg-bogus "top-level comma expression in array subscript is deprecated" } + a[(int{(1,2)}, int{})]; +} diff --git gcc/testsuite/g++.dg/cpp2a/comma3.C gcc/testsuite/g++.dg/cpp2a/comma3.C new file mode 100644 index 00000000000..59a518382d7 --- /dev/null +++ gcc/testsuite/g++.dg/cpp2a/comma3.C @@ -0,0 +1,27 @@ +// PR c++/91338 - P1161R3: Deprecate a[b,c]. +// { dg-do compile { target c++11 } } +// { dg-options "-Wcomma-subscript" } + +struct S { + int operator,(int) { return 42; } +}; + +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)]; + + a[(void) b, c]; // { dg-warning "top-level comma expression in array subscript is deprecated" } + 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)]; + + a[S(), 10]; // { dg-warning "top-level comma expression in array subscript is deprecated" } + 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{})]; +}