diff mbox series

[C++] Fix up parsing of T (__attribute__(()) fn) (int) in C++17 mode (PR c++/92438)

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

Commit Message

Jakub Jelinek Dec. 20, 2019, 11:27 p.m. UTC
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  <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.


	Jakub

Comments

Jason Merrill Dec. 26, 2019, 3:45 a.m. UTC | #1
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
>
diff mbox series

Patch

--- 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);
+}