Message ID | 20191220232757.GB10088@tucnak |
---|---|
State | New |
Headers | show |
Series | [C++] Fix up parsing of T (__attribute__(()) fn) (int) in C++17 mode (PR c++/92438) | expand |
On 12/20/19 6:27 PM, Jakub Jelinek wrote: > 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? OK. > 2019-12-20 Jakub Jelinek <jakub@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 >
--- 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); +}