diff options
author | Iain Sandoe <iain@sandoe.co.uk> | 2021-02-26 10:21:02 +0000 |
---|---|---|
committer | Iain Sandoe <iain@sandoe.co.uk> | 2021-03-18 11:47:27 +0000 |
commit | 0cc218d42c241ed286cc5af9fb7d2e45386f7a24 (patch) | |
tree | c678573b7b4061649f125fcc6568d4b8827e6dcc | |
parent | 8f0c9d53ef3a9b8ba2579b53596cc2b7f5d8bf69 (diff) | |
download | gcc-0cc218d42c241ed286cc5af9fb7d2e45386f7a24.zip gcc-0cc218d42c241ed286cc5af9fb7d2e45386f7a24.tar.gz gcc-0cc218d42c241ed286cc5af9fb7d2e45386f7a24.tar.bz2 |
Objective-C++ : Fix handling of unnamed message parms [PR49070].
When we are parsing an Objective-C++ message, a colon is a valid
terminator for a assignment-expression. That is:
[receiver meth:x:x:x:x];
Is a valid, if somewhat unreadable, construction; corresponding
to a method declaration like:
- (id) meth:(id)arg0 :(id)arg1 :(id)arg2 :(id)arg3;
Where three of the message params have no selector name.
If fact, although it might be unintentional, Objective-C/C++ can
accept message selectors with all the parms unnamed (this applies
to the clang implementation too, which is taken as the reference
for the language).
For regular C++, the pattern x:x is not valid in that position an
an error is emitted with a fixit for the expected scope token.
If we simply made that error conditional on !c_dialect_objc()
that would regress Objective-C++ diagnostics for cases outside a
message selector, so we add a state flag for this.
gcc/cp/ChangeLog:
PR objc++/49070
* parser.c (cp_debug_parser): Add Objective-C++ message
state flag.
(cp_parser_nested_name_specifier_opt): Allow colon to
terminate an assignment-expression when parsing Objective-
C++ messages.
(cp_parser_objc_message_expression): Set and clear message
parsing state on entry and exit.
* parser.h (struct cp_parser): Add a context flag for
Objective-C++ message state.
gcc/testsuite/ChangeLog:
PR objc++/49070
* obj-c++.dg/pr49070.mm: New test.
* objc.dg/unnamed-parms.m: New test.
-rw-r--r-- | gcc/cp/parser.c | 8 | ||||
-rw-r--r-- | gcc/cp/parser.h | 4 | ||||
-rw-r--r-- | gcc/testsuite/obj-c++.dg/pr49070.mm | 52 | ||||
-rw-r--r-- | gcc/testsuite/objc.dg/unnamed-parms.m | 28 |
4 files changed, 91 insertions, 1 deletions
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c index 0a7d18a..d4e4859 100644 --- a/gcc/cp/parser.c +++ b/gcc/cp/parser.c @@ -572,6 +572,8 @@ cp_debug_parser (FILE *file, cp_parser *parser) parser->colon_corrects_to_scope_p); cp_debug_print_flag (file, "Colon doesn't start a class definition", parser->colon_doesnt_start_class_def_p); + cp_debug_print_flag (file, "Parsing an Objective-C++ message context", + parser->objective_c_message_context_p); if (parser->type_definition_forbidden_message) fprintf (file, "Error message for forbidden type definitions: %s %s\n", parser->type_definition_forbidden_message, @@ -6626,7 +6628,9 @@ cp_parser_nested_name_specifier_opt (cp_parser *parser, if (token->type == CPP_COLON && parser->colon_corrects_to_scope_p - && cp_lexer_peek_nth_token (parser->lexer, 3)->type == CPP_NAME) + && cp_lexer_peek_nth_token (parser->lexer, 3)->type == CPP_NAME + /* name:name is a valid sequence in an Objective C message. */ + && !parser->objective_c_message_context_p) { gcc_rich_location richloc (token->location); richloc.add_fixit_replace ("::"); @@ -33034,6 +33038,7 @@ cp_parser_objc_message_expression (cp_parser* parser) { tree receiver, messageargs; + parser->objective_c_message_context_p = true; location_t start_loc = cp_lexer_peek_token (parser->lexer)->location; cp_lexer_consume_token (parser->lexer); /* Eat '['. */ receiver = cp_parser_objc_message_receiver (parser); @@ -33050,6 +33055,7 @@ cp_parser_objc_message_expression (cp_parser* parser) location_t combined_loc = make_location (start_loc, start_loc, end_loc); protected_set_expr_location (result, combined_loc); + parser->objective_c_message_context_p = false; return result; } diff --git a/gcc/cp/parser.h b/gcc/cp/parser.h index d587cf2..a468b69 100644 --- a/gcc/cp/parser.h +++ b/gcc/cp/parser.h @@ -350,6 +350,10 @@ struct GTY(()) cp_parser { is terminated by colon. */ bool colon_doesnt_start_class_def_p; + /* TRUE if we are parsing an objective c message, and ':' is permitted + to terminate an assignment-expression. */ + bool objective_c_message_context_p; + /* If non-NULL, then we are parsing a construct where new type definitions are not permitted. The string stored here will be issued as an error message if a type is defined. */ diff --git a/gcc/testsuite/obj-c++.dg/pr49070.mm b/gcc/testsuite/obj-c++.dg/pr49070.mm new file mode 100644 index 0000000..d67c721 --- /dev/null +++ b/gcc/testsuite/obj-c++.dg/pr49070.mm @@ -0,0 +1,52 @@ +/* Only needs to compile. */ +/* { dg-additional-options "-std=c++11" } */ + +#ifdef __cplusplus +enum X { + x = 5, + y +}; +#endif + +__attribute__((__objc_root_class__)) +@interface A +- (id) :(id)arg0 :(id)arg1; +- (id) m:(id)arg0 :(id)arg1 :(id)arg2 :(id)arg3; +#ifdef __cplusplus +- (id) n:(X)arg0 :(X)arg1 :(id)arg2 :(id)arg3; +#endif +@end + +@implementation A +- (id) :(id)arg0 :(id)arg1 +{ + return arg1; +} +- (id) m:(id)arg0 :(id)arg1 :(id)arg2 :(id)arg3 +{ + return arg2; +} +#ifdef __cplusplus +- (id) n:(X)arg0 :(X)arg1 :(id)arg2 :(id)arg3 +{ + return arg2; +} +#endif +@end + +id f1 (A *x) +{ + return [x:x:x]; +} + +id f2 (A *x) +{ + return [x m:x:x:x:x]; +} + +#ifdef __cplusplus +id f3 (A *x) +{ + return [x n:X::x:X::y:x:x]; +} +#endif diff --git a/gcc/testsuite/objc.dg/unnamed-parms.m b/gcc/testsuite/objc.dg/unnamed-parms.m new file mode 100644 index 0000000..22426cf --- /dev/null +++ b/gcc/testsuite/objc.dg/unnamed-parms.m @@ -0,0 +1,28 @@ +/* Only needs to compile [see PR 49070 for C++ issue]. */ + +__attribute__((__objc_root_class__)) +@interface A +- (id) :(id)arg0 :(id)arg1; +- (id) m:(id)arg0 :(id)arg1 :(id)arg2 :(id)arg3; +@end + +@implementation A +- (id) :(id)arg0 :(id)arg1 +{ + return arg1; +} +- (id) m:(id)arg0 :(id)arg1 :(id)arg2 :(id)arg3 +{ + return arg2; +} +@end + +id f1 (A *x) +{ + return [x:x:x]; +} + +id f2 (A *x) +{ + return [x m:x:x:x:x]; +} |