Hi! In C++17/2a mode, cp_parser_constructor_declarator_p because of deduction guides considers constructor_p in more cases and returns true on typedef struct S { int x; } T; T (__attribute__((unused)) qux) (T x); just because constructor arguments may start with __attribute__ keyword (as the testcase tests, yes, they can, but parenthesized declarator can too), and so we reject the above declaration, even when it is valid and accepted in C++98/11/14 modes too. The testcase shows this causing problems already before, e.g. declaring methods with parenthesized declarator starting with attribute used to be considered as constructor and rejected for quite a while.
Fixed thusly, bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk? 2019-12-20 Jakub Jelinek <ja...@redhat.com> PR c++/92438 * parser.c (cp_parser_constructor_declarator_p): If open paren is followed by RID_ATTRIBUTE, skip over the attribute tokens and try to parse type specifier. * g++.dg/ext/attrib61.C: New test. --- gcc/cp/parser.c.jj 2019-12-20 17:51:44.979483292 +0100 +++ gcc/cp/parser.c 2019-12-20 19:15:40.011165334 +0100 @@ -28493,7 +28493,15 @@ cp_parser_constructor_declarator_p (cp_p /* A parameter declaration begins with a decl-specifier, which is either the "attribute" keyword, a storage class specifier, or (usually) a type-specifier. */ - && !cp_lexer_next_token_is_decl_specifier_keyword (parser->lexer) + && (!cp_lexer_next_token_is_decl_specifier_keyword (parser->lexer) + /* GNU attributes can actually appear both at the start of + a parameter and parenthesized declarator. + S (__attribute__((unused)) int); + is a constructor, but + S (__attribute__((unused)) foo) (int); + is a function declaration. */ + || (cp_parser_allow_gnu_extensions_p (parser) + && cp_next_tokens_can_be_gnu_attribute_p (parser))) /* A parameter declaration can also begin with [[attribute]]. */ && !cp_next_tokens_can_be_std_attribute_p (parser)) { @@ -28501,6 +28509,13 @@ cp_parser_constructor_declarator_p (cp_p tree pushed_scope = NULL_TREE; unsigned saved_num_template_parameter_lists; + if (cp_next_tokens_can_be_gnu_attribute_p (parser)) + { + unsigned int n = cp_parser_skip_gnu_attributes_opt (parser, 1); + while (--n) + cp_lexer_consume_token (parser->lexer); + } + /* Names appearing in the type-specifier should be looked up in the scope of the class. */ if (current_class_type) --- gcc/testsuite/g++.dg/ext/attrib61.C.jj 2019-12-20 19:22:30.073916272 +0100 +++ gcc/testsuite/g++.dg/ext/attrib61.C 2019-12-20 19:19:44.325450755 +0100 @@ -0,0 +1,26 @@ +// PR c++/92438 +// { dg-do compile } + +typedef struct S { int x; } T; +T (foo) (T x); +T __attribute__((unused)) bar (T x); +struct S (__attribute__((unused)) baz) (T x); +T (__attribute__((unused)) qux) (T x); + +struct U +{ + U (__attribute__((unused)) int); + U (__attribute__((unused)) corge) (int); +}; + +void +test () +{ + T a, b; + a = foo (b); + b = bar (a); + a = baz (b); + b = qux (a); + U u (5); + U v = u.corge (3); +} Jakub