c++: Reject attributes without arguments used as pack expansion [PR109756]

Message ID ZFoAggM58+P5CFlE@tucnak
State Unresolved
Headers
Series c++: Reject attributes without arguments used as pack expansion [PR109756] |

Checks

Context Check Description
snail/gcc-patch-check warning Git am fail log

Commit Message

Jakub Jelinek May 9, 2023, 8:12 a.m. UTC
  Hi!

The following testcase shows we silently accept (and ignore) attributes without
arguments used as pack expansions.  This is because we call
make_pack_expansion and that starts with
  if (!arg || arg == error_mark_node)
    return arg;
Now, an attribute without arguments like [[noreturn...]] is IMHO always
invalid, in this case for 2 reasons; one is that as it has no arguments,
no pack can be present and second is that the standard says that
attributes need to specially permit uses of parameter pack and doesn't
explicitly permit it for any of the standard attributes (except for alignas?
which has different syntax).
If an attribute has some arguments but doesn't contain packs in those
arguments, make_pack_expansion will already diagnose it.

My first version of the patch just added the last hunk in parser.cc and
without the && !has_args, but that unfortunately regresses
gen-attrs-65.C - TREE_VALUE (attribute) == NULL_TREE can mean a recognized
or unrecognized attribute which doesn't have any arguments, but can also
mean an unrecognized attribute which had arguments but which we didn't
parse because we didn't know how to parse those arguments.

So, the patch remembers from cp_parser_std_attribute whether an attribute
had or didn't have arguments and diagnoses it just in the latter case.

Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?
I guess this isn't appropriate for backports as accepts-invalid.

2023-05-09  Jakub Jelinek  <jakub@redhat.com>

	PR c++/109756
	* parser.cc (cp_parser_std_attribute): Add HAS_ARGS argument,
	set it to true iff the attribute has arguments specified.
	(cp_parser_std_attribute_list): Adjust caller.  If ... is used
	after attribute without arguments, diagnose it and return
	error_mark_node.

	* g++.dg/cpp0x/gen-attrs-78.C: New test.


	Jakub
  

Comments

Jason Merrill May 9, 2023, 5:17 p.m. UTC | #1
On 5/9/23 04:12, Jakub Jelinek wrote:
> Hi!
> 
> The following testcase shows we silently accept (and ignore) attributes without
> arguments used as pack expansions.  This is because we call
> make_pack_expansion and that starts with
>    if (!arg || arg == error_mark_node)
>      return arg;
> Now, an attribute without arguments like [[noreturn...]] is IMHO always
> invalid, in this case for 2 reasons; one is that as it has no arguments,
> no pack can be present and second is that the standard says that
> attributes need to specially permit uses of parameter pack and doesn't
> explicitly permit it for any of the standard attributes (except for alignas?
> which has different syntax).
> If an attribute has some arguments but doesn't contain packs in those
> arguments, make_pack_expansion will already diagnose it.
> 
> My first version of the patch just added the last hunk in parser.cc and
> without the && !has_args, but that unfortunately regresses
> gen-attrs-65.C - TREE_VALUE (attribute) == NULL_TREE can mean a recognized
> or unrecognized attribute which doesn't have any arguments, but can also
> mean an unrecognized attribute which had arguments but which we didn't
> parse because we didn't know how to parse those arguments.

How about changing cp_parser_std_attribute to set TREE_VALUE to 
error_mark_node if it skips arguments?

> So, the patch remembers from cp_parser_std_attribute whether an attribute
> had or didn't have arguments and diagnoses it just in the latter case.
> 
> Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?
> I guess this isn't appropriate for backports as accepts-invalid.
> 
> 2023-05-09  Jakub Jelinek  <jakub@redhat.com>
> 
> 	PR c++/109756
> 	* parser.cc (cp_parser_std_attribute): Add HAS_ARGS argument,
> 	set it to true iff the attribute has arguments specified.
> 	(cp_parser_std_attribute_list): Adjust caller.  If ... is used
> 	after attribute without arguments, diagnose it and return
> 	error_mark_node.
> 
> 	* g++.dg/cpp0x/gen-attrs-78.C: New test.
> 
> --- gcc/cp/parser.cc.jj	2023-04-25 16:40:42.010723809 +0200
> +++ gcc/cp/parser.cc	2023-05-08 12:47:58.767924380 +0200
> @@ -2634,7 +2634,7 @@ static tree cp_parser_gnu_attributes_opt
>   static tree cp_parser_gnu_attribute_list
>     (cp_parser *, bool = false);
>   static tree cp_parser_std_attribute
> -  (cp_parser *, tree);
> +  (cp_parser *, tree, bool &);
>   static tree cp_parser_std_attribute_spec
>     (cp_parser *);
>   static tree cp_parser_std_attribute_spec_seq
> @@ -29313,7 +29313,7 @@ cp_parser_omp_sequence_args (cp_parser *
>         { balanced-token-seq }.  */
>   
>   static tree
> -cp_parser_std_attribute (cp_parser *parser, tree attr_ns)
> +cp_parser_std_attribute (cp_parser *parser, tree attr_ns, bool &has_args)
>   {
>     tree attribute, attr_id = NULL_TREE, arguments;
>     cp_token *token;
> @@ -29323,6 +29323,7 @@ cp_parser_std_attribute (cp_parser *pars
>   
>     /* First, parse name of the attribute, a.k.a attribute-token.  */
>   
> +  has_args = false;
>     token = cp_lexer_peek_token (parser->lexer);
>     if (token->type == CPP_NAME)
>       attr_id = token->u.value;
> @@ -29419,6 +29420,7 @@ cp_parser_std_attribute (cp_parser *pars
>         return attribute;
>       }
>   
> +  has_args = true;
>     {
>       vec<tree, va_gc> *vec;
>       int attr_flag = normal_attr;
> @@ -29544,7 +29546,8 @@ cp_parser_std_attribute_list (cp_parser
>     while (true)
>       {
>         location_t loc = cp_lexer_peek_token (parser->lexer)->location;
> -      attribute = cp_parser_std_attribute (parser, attr_ns);
> +      bool has_args;
> +      attribute = cp_parser_std_attribute (parser, attr_ns, has_args);
>         if (attribute == error_mark_node)
>   	break;
>         if (attribute != NULL_TREE)
> @@ -29562,6 +29565,12 @@ cp_parser_std_attribute_list (cp_parser
>   	  if (attribute == NULL_TREE)
>   	    error_at (token->location,
>   		      "expected attribute before %<...%>");
> +	  else if (TREE_VALUE (attribute) == NULL_TREE && !has_args)
> +	    {
> +	      error_at (token->location, "attribute with no arguments "
> +					 "contains no parameter packs");
> +	      return error_mark_node;
> +	    }
>   	  else
>   	    {
>   	      tree pack = make_pack_expansion (TREE_VALUE (attribute));
> --- gcc/testsuite/g++.dg/cpp0x/gen-attrs-78.C.jj	2023-05-08 12:33:13.387581760 +0200
> +++ gcc/testsuite/g++.dg/cpp0x/gen-attrs-78.C	2023-05-08 12:32:23.146301128 +0200
> @@ -0,0 +1,29 @@
> +// PR c++/109756
> +// { dg-do compile { target c++11 } }
> +// { dg-options "-Wno-attributes" }
> +
> +template <int ...args>
> +[[noreturn...]]					// { dg-error "attribute with no arguments contains no parameter packs" }
> +[[deprecated...]]				// { dg-error "attribute with no arguments contains no parameter packs" }
> +[[nodiscard...]]				// { dg-error "attribute with no arguments contains no parameter packs" }
> +int foo (int x)
> +{
> +  switch (x)
> +    {
> +    case 1:
> +      [[likely...]];				// { dg-error "attribute with no arguments contains no parameter packs" }
> +      [[fallthrough...]];			// { dg-error "attribute with no arguments contains no parameter packs" }
> +    case 2:
> +      [[unlikely...]];				// { dg-error "attribute with no arguments contains no parameter packs" }
> +
> +      break;
> +    default:
> +      break;
> +    }
> +  struct T {};
> +  struct S { [[no_unique_address...]] T t; };	// { dg-error "attribute with no arguments contains no parameter packs" }
> +  for (;;)
> +    ;
> +}
> +
> +int a = foo <1, 2, 3> (4);
> 
> 	Jakub
>
  

Patch

--- gcc/cp/parser.cc.jj	2023-04-25 16:40:42.010723809 +0200
+++ gcc/cp/parser.cc	2023-05-08 12:47:58.767924380 +0200
@@ -2634,7 +2634,7 @@  static tree cp_parser_gnu_attributes_opt
 static tree cp_parser_gnu_attribute_list
   (cp_parser *, bool = false);
 static tree cp_parser_std_attribute
-  (cp_parser *, tree);
+  (cp_parser *, tree, bool &);
 static tree cp_parser_std_attribute_spec
   (cp_parser *);
 static tree cp_parser_std_attribute_spec_seq
@@ -29313,7 +29313,7 @@  cp_parser_omp_sequence_args (cp_parser *
       { balanced-token-seq }.  */
 
 static tree
-cp_parser_std_attribute (cp_parser *parser, tree attr_ns)
+cp_parser_std_attribute (cp_parser *parser, tree attr_ns, bool &has_args)
 {
   tree attribute, attr_id = NULL_TREE, arguments;
   cp_token *token;
@@ -29323,6 +29323,7 @@  cp_parser_std_attribute (cp_parser *pars
 
   /* First, parse name of the attribute, a.k.a attribute-token.  */
 
+  has_args = false;
   token = cp_lexer_peek_token (parser->lexer);
   if (token->type == CPP_NAME)
     attr_id = token->u.value;
@@ -29419,6 +29420,7 @@  cp_parser_std_attribute (cp_parser *pars
       return attribute;
     }
 
+  has_args = true;
   {
     vec<tree, va_gc> *vec;
     int attr_flag = normal_attr;
@@ -29544,7 +29546,8 @@  cp_parser_std_attribute_list (cp_parser
   while (true)
     {
       location_t loc = cp_lexer_peek_token (parser->lexer)->location;
-      attribute = cp_parser_std_attribute (parser, attr_ns);
+      bool has_args;
+      attribute = cp_parser_std_attribute (parser, attr_ns, has_args);
       if (attribute == error_mark_node)
 	break;
       if (attribute != NULL_TREE)
@@ -29562,6 +29565,12 @@  cp_parser_std_attribute_list (cp_parser
 	  if (attribute == NULL_TREE)
 	    error_at (token->location,
 		      "expected attribute before %<...%>");
+	  else if (TREE_VALUE (attribute) == NULL_TREE && !has_args)
+	    {
+	      error_at (token->location, "attribute with no arguments "
+					 "contains no parameter packs");
+	      return error_mark_node;
+	    }
 	  else
 	    {
 	      tree pack = make_pack_expansion (TREE_VALUE (attribute));
--- gcc/testsuite/g++.dg/cpp0x/gen-attrs-78.C.jj	2023-05-08 12:33:13.387581760 +0200
+++ gcc/testsuite/g++.dg/cpp0x/gen-attrs-78.C	2023-05-08 12:32:23.146301128 +0200
@@ -0,0 +1,29 @@ 
+// PR c++/109756
+// { dg-do compile { target c++11 } }
+// { dg-options "-Wno-attributes" }
+
+template <int ...args>
+[[noreturn...]]					// { dg-error "attribute with no arguments contains no parameter packs" }
+[[deprecated...]]				// { dg-error "attribute with no arguments contains no parameter packs" }
+[[nodiscard...]]				// { dg-error "attribute with no arguments contains no parameter packs" }
+int foo (int x)
+{
+  switch (x)
+    {
+    case 1:
+      [[likely...]];				// { dg-error "attribute with no arguments contains no parameter packs" }
+      [[fallthrough...]];			// { dg-error "attribute with no arguments contains no parameter packs" }
+    case 2:
+      [[unlikely...]];				// { dg-error "attribute with no arguments contains no parameter packs" }
+
+      break;
+    default:
+      break;
+    }
+  struct T {};
+  struct S { [[no_unique_address...]] T t; };	// { dg-error "attribute with no arguments contains no parameter packs" }
+  for (;;)
+    ;
+}
+
+int a = foo <1, 2, 3> (4);