c++: Implement C++26 P0609R3 - Attributes for Structured Bindings [PR114456]

Message ID Zi9IPOngOU/UWePb@tucnak
State New
Headers
Series c++: Implement C++26 P0609R3 - Attributes for Structured Bindings [PR114456] |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_gcc_build--master-arm success Testing passed
linaro-tcwg-bot/tcwg_gcc_check--master-arm success Testing passed
linaro-tcwg-bot/tcwg_gcc_build--master-aarch64 success Testing passed
linaro-tcwg-bot/tcwg_gcc_check--master-aarch64 success Testing passed

Commit Message

Jakub Jelinek April 29, 2024, 7:11 a.m. UTC
  Hi!

The following patch implements the P0609R3 paper; we build the
VAR_DECLs for the structured binding identifiers early, so all we need
IMHO is just to parse the attributed identifier list and pass the attributes
to the VAR_DECL creation.

The paper mentions maybe_unused and gnu::nonstring attributes as examples
where they can be useful.  Not sure about either of them.
For maybe_unused, the thing is that both GCC and clang already don't
diagnose maybe unused for the structured binding identifiers, because it
would be a false positive too often; and there is no easy way to find out
if a structured binding has been written with the P0609R3 paper in mind or
not (maybe we could turn it on if in the structured binding is any
attribute, even if just [[]] and record that as a flag on the whole
underlying decl, so that we'd diagnose
  auto [a, b, c[[]]] = d;
  // use a, c but not b
but not
  auto [e, f, g] = d;
  // use a, c but not b
).  For gnu::nonstring, the issue is that we currently don't allow the
attribute on references to char * or references to char[], just on
char */char[].  I've filed a PR for that.

The first testcase in the patch tests it on [[]] and [[maybe_unused]],
just whether it is parsed properly, second on gnu::deprecated, which
works.  Haven't used deprecated attribute because the paper said that
attribute is for further investigation.

Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

2024-04-29  Jakub Jelinek  <jakub@redhat.com>

	PR c++/114456
gcc/c-family/
	* c-cppbuiltin.cc (c_cpp_builtins): Predefine
	__cpp_structured_bindings for C++26 to 202403L rather than
	201606L.
gcc/cp/
	* parser.cc (cp_parser_decomposition_declaration): Implement C++26
	P0609R3 - Attributes for Structured Bindings.  Parse attributed
	identifier lists for structured binding declarations, pass the
	attributes to start_decl.
gcc/testsuite/
	* g++.dg/cpp26/decomp1.C: New test.
	* g++.dg/cpp26/decomp2.C: New test.
	* g++.dg/cpp26/feat-cxx26.C (__cpp_structured_bindings): Expect
	202403 rather than 201606.


	Jakub
  

Comments

Jason Merrill April 30, 2024, 12:13 a.m. UTC | #1
On 4/29/24 00:11, Jakub Jelinek wrote:
> Hi!
> 
> The following patch implements the P0609R3 paper; we build the
> VAR_DECLs for the structured binding identifiers early, so all we need
> IMHO is just to parse the attributed identifier list and pass the attributes
> to the VAR_DECL creation.
> 
> The paper mentions maybe_unused and gnu::nonstring attributes as examples
> where they can be useful.  Not sure about either of them.
> For maybe_unused, the thing is that both GCC and clang already don't
> diagnose maybe unused for the structured binding identifiers, because it
> would be a false positive too often; and there is no easy way to find out
> if a structured binding has been written with the P0609R3 paper in mind or
> not (maybe we could turn it on if in the structured binding is any
> attribute, even if just [[]] and record that as a flag on the whole
> underlying decl, so that we'd diagnose
>    auto [a, b, c[[]]] = d;
>    // use a, c but not b
> but not
>    auto [e, f, g] = d;
>    // use a, c but not b
> ).  For gnu::nonstring, the issue is that we currently don't allow the
> attribute on references to char * or references to char[], just on
> char */char[].  I've filed a PR for that.
> 
> The first testcase in the patch tests it on [[]] and [[maybe_unused]],
> just whether it is parsed properly, second on gnu::deprecated, which
> works.  Haven't used deprecated attribute because the paper said that
> attribute is for further investigation.
> 
> Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

OK.

> 2024-04-29  Jakub Jelinek  <jakub@redhat.com>
> 
> 	PR c++/114456
> gcc/c-family/
> 	* c-cppbuiltin.cc (c_cpp_builtins): Predefine
> 	__cpp_structured_bindings for C++26 to 202403L rather than
> 	201606L.
> gcc/cp/
> 	* parser.cc (cp_parser_decomposition_declaration): Implement C++26
> 	P0609R3 - Attributes for Structured Bindings.  Parse attributed
> 	identifier lists for structured binding declarations, pass the
> 	attributes to start_decl.
> gcc/testsuite/
> 	* g++.dg/cpp26/decomp1.C: New test.
> 	* g++.dg/cpp26/decomp2.C: New test.
> 	* g++.dg/cpp26/feat-cxx26.C (__cpp_structured_bindings): Expect
> 	202403 rather than 201606.
> 
> --- gcc/cp/parser.cc.jj	2024-04-26 11:42:24.653016208 +0200
> +++ gcc/cp/parser.cc	2024-04-26 13:59:17.791482874 +0200
> @@ -16075,13 +16075,37 @@ cp_parser_decomposition_declaration (cp_
>   
>     /* Parse the identifier-list.  */
>     auto_vec<cp_expr, 10> v;
> +  bool attr_diagnosed = false;
> +  int first_attr = -1;
> +  unsigned int cnt = 0;
>     if (!cp_lexer_next_token_is (parser->lexer, CPP_CLOSE_SQUARE))
>       while (true)
>         {
>   	cp_expr e = cp_parser_identifier (parser);
>   	if (e.get_value () == error_mark_node)
>   	  break;
> +	tree attr = NULL_TREE;
> +	if (cp_next_tokens_can_be_std_attribute_p (parser))
> +	  {
> +	    if (cxx_dialect >= cxx17 && cxx_dialect < cxx26 && !attr_diagnosed)
> +	      {
> +		pedwarn (cp_lexer_peek_token (parser->lexer)->location,
> +			 OPT_Wc__26_extensions,
> +			 "structured bindings with attributed identifiers "
> +			 "only available with %<-std=c++2c%> or "
> +			 "%<-std=gnu++2c%>");
> +		attr_diagnosed = true;
> +	      }
> +	    attr = cp_parser_std_attribute_spec_seq (parser);
> +	    if (attr == error_mark_node)
> +	      attr = NULL_TREE;
> +	    if (attr && first_attr == -1)
> +	      first_attr = v.length ();
> +	  }
>   	v.safe_push (e);
> +	++cnt;
> +	if (first_attr != -1)
> +	  v.safe_push (attr);
>   	if (!cp_lexer_next_token_is (parser->lexer, CPP_COMMA))
>   	  break;
>   	cp_lexer_consume_token (parser->lexer);
> @@ -16139,8 +16163,11 @@ cp_parser_decomposition_declaration (cp_
>   	  declarator->id_loc = e.get_location ();
>   	}
>         tree elt_pushed_scope;
> +      tree attr = NULL_TREE;
> +      if (first_attr != -1 && i >= (unsigned) first_attr)
> +	attr = v[++i].get_value ();
>         tree decl2 = start_decl (declarator, &decl_specs, SD_DECOMPOSITION,
> -			       NULL_TREE, NULL_TREE, &elt_pushed_scope);
> +			       NULL_TREE, attr, &elt_pushed_scope);
>         if (decl2 == error_mark_node)
>   	decl = error_mark_node;
>         else if (decl != error_mark_node && DECL_CHAIN (decl2) != prev)
> @@ -16183,7 +16210,7 @@ cp_parser_decomposition_declaration (cp_
>   
>         if (decl != error_mark_node)
>   	{
> -	  cp_decomp decomp = { prev, v.length () };
> +	  cp_decomp decomp = { prev, cnt };
>   	  cp_finish_decl (decl, initializer, non_constant_p, NULL_TREE,
>   			  (is_direct_init ? LOOKUP_NORMAL : LOOKUP_IMPLICIT),
>   			  &decomp);
> @@ -16193,7 +16220,7 @@ cp_parser_decomposition_declaration (cp_
>     else if (decl != error_mark_node)
>       {
>         *maybe_range_for_decl = prev;
> -      cp_decomp decomp = { prev, v.length () };
> +      cp_decomp decomp = { prev, cnt };
>         /* Ensure DECL_VALUE_EXPR is created for all the decls but
>   	 the underlying DECL.  */
>         cp_finish_decomp (decl, &decomp);
> --- gcc/c-family/c-cppbuiltin.cc.jj	2024-04-26 11:42:24.621016651 +0200
> +++ gcc/c-family/c-cppbuiltin.cc	2024-04-26 13:42:50.559184222 +0200
> @@ -1044,7 +1044,8 @@ c_cpp_builtins (cpp_reader *pfile)
>   	  /* Old macro, superseded by
>   	     __cpp_nontype_template_parameter_auto.  */
>   	  cpp_define (pfile, "__cpp_template_auto=201606L");
> -	  cpp_define (pfile, "__cpp_structured_bindings=201606L");
> +	  if (cxx_dialect <= cxx23)
> +	    cpp_define (pfile, "__cpp_structured_bindings=201606L");
>   	  cpp_define (pfile, "__cpp_variadic_using=201611L");
>   	  cpp_define (pfile, "__cpp_guaranteed_copy_elision=201606L");
>   	  cpp_define (pfile, "__cpp_nontype_template_parameter_auto=201606L");
> @@ -1090,6 +1091,7 @@ c_cpp_builtins (cpp_reader *pfile)
>   	  cpp_define (pfile, "__cpp_constexpr=202306L");
>   	  cpp_define (pfile, "__cpp_static_assert=202306L");
>   	  cpp_define (pfile, "__cpp_placeholder_variables=202306L");
> +	  cpp_define (pfile, "__cpp_structured_bindings=202403L");
>   	}
>         if (flag_concepts)
>           {
> --- gcc/testsuite/g++.dg/cpp26/decomp1.C.jj	2024-04-26 13:42:50.559184222 +0200
> +++ gcc/testsuite/g++.dg/cpp26/decomp1.C	2024-04-26 14:24:59.276090634 +0200
> @@ -0,0 +1,33 @@
> +// { dg-do compile { target c++11 } }
> +// { dg-options "" }
> +
> +namespace std {
> +  template<typename T> struct tuple_size;
> +  template<int, typename> struct tuple_element;
> +}
> +
> +struct A {
> +  int i;
> +  template <int I> int& get() { return i; }
> +};
> +
> +template<> struct std::tuple_size<A> { static const int value = 3; };
> +template<int I> struct std::tuple_element<I,A> { using type = int; };
> +
> +struct B {
> +  int i, j;
> +  long long k, l;
> +} z[6];
> +
> +void
> +foo (A &a, B &b)
> +{
> +  auto [ c [[]], d, e [[maybe_unused]] ] = a;		// { dg-warning "structured bindings with attributed identifiers only available with" "" { target { c++17 && c++23_down } } }
> +							// { dg-warning "structured bindings only available with" "" { target c++14_down } .-1 }
> +  auto [ f, h [[maybe_unused]] [[]], i [[]], j ] = b;	// { dg-warning "structured bindings with attributed identifiers only available with" "" { target { c++17 && c++23_down } } }
> +							// { dg-warning "structured bindings only available with" "" { target c++14_down } .-1 }
> +  for (auto [ k, l [[maybe_unused]], m, n [[]]] : z)	// { dg-warning "structured bindings with attributed identifiers only available with" "" { target { c++17 && c++23_down } } }
> +    ;							// { dg-warning "structured bindings only available with" "" { target c++14_down } .-1 }
> +  auto &[o[[]][[]][[]], p[[]], q[[]]] = a;		// { dg-warning "structured bindings with attributed identifiers only available with" "" { target { c++17 && c++23_down } } }
> +    ;							// { dg-warning "structured bindings only available with" "" { target c++14_down } .-1 }
> +}
> --- gcc/testsuite/g++.dg/cpp26/decomp2.C.jj	2024-04-26 14:21:31.454974172 +0200
> +++ gcc/testsuite/g++.dg/cpp26/decomp2.C	2024-04-26 14:28:11.665421202 +0200
> @@ -0,0 +1,46 @@
> +// { dg-do compile { target c++11 } }
> +// { dg-options "" }
> +
> +namespace std {
> +  template<typename T> struct tuple_size;
> +  template<int, typename> struct tuple_element;
> +}
> +
> +struct A {
> +  int i;
> +  template <int I> int& get() { return i; }
> +};
> +
> +template<> struct std::tuple_size<A> { static const int value = 3; };
> +template<int I> struct std::tuple_element<I,A> { using type = int; };
> +
> +struct B {
> +  int i, j;
> +  long long k, l;
> +} z[6];
> +
> +void
> +foo (A &a, B &b)
> +{
> +  auto [ c [[]], d, e [[gnu::deprecated]] ] = a;	// { dg-warning "structured bindings with attributed identifiers only available with" "" { target { c++17 && c++23_down } } }
> +							// { dg-warning "structured bindings only available with" "" { target c++14_down } .-1 }
> +							// { dg-message "declared here" "" { target *-*-* } .-2 }
> +  ++c;
> +  ++d;
> +  ++e;							// { dg-warning "'e' is deprecated" }
> +  auto [ f, h [[gnu::deprecated]] [[]], i [[]], j ] = b;// { dg-warning "structured bindings with attributed identifiers only available with" "" { target { c++17 && c++23_down } } }
> +							// { dg-warning "structured bindings only available with" "" { target c++14_down } .-1 }
> +							// { dg-message "declared here" "" { target *-*-* } .-2 }
> +  ++f;
> +  ++h;							// { dg-warning "'h' is deprecated" }
> +  ++i;
> +  ++j;
> +  for (auto [ k, l [[gnu::deprecated]], m, n [[]]] : z)	// { dg-warning "structured bindings with attributed identifiers only available with" "" { target { c++17 && c++23_down } } }
> +    {							// { dg-warning "structured bindings only available with" "" { target c++14_down } .-1 }
> +							// { dg-message "declared here" "" { target *-*-* } .-2 }
> +      ++k;
> +      ++l;						// { dg-warning "'l' is deprecated" }
> +      ++m;
> +      ++n;
> +    }
> +}
> --- gcc/testsuite/g++.dg/cpp26/feat-cxx26.C.jj	2024-04-26 11:42:24.675015903 +0200
> +++ gcc/testsuite/g++.dg/cpp26/feat-cxx26.C	2024-04-26 13:42:50.559184222 +0200
> @@ -394,8 +394,8 @@
>   
>   #ifndef __cpp_structured_bindings
>   #  error "__cpp_structured_bindings"
> -#elif __cpp_structured_bindings != 201606
> -#  error "__cpp_structured_bindings != 201606"
> +#elif __cpp_structured_bindings != 202403
> +#  error "__cpp_structured_bindings != 202403"
>   #endif
>   
>   #ifndef __cpp_template_template_args
> 
> 	Jakub
>
  

Patch

--- gcc/cp/parser.cc.jj	2024-04-26 11:42:24.653016208 +0200
+++ gcc/cp/parser.cc	2024-04-26 13:59:17.791482874 +0200
@@ -16075,13 +16075,37 @@  cp_parser_decomposition_declaration (cp_
 
   /* Parse the identifier-list.  */
   auto_vec<cp_expr, 10> v;
+  bool attr_diagnosed = false;
+  int first_attr = -1;
+  unsigned int cnt = 0;
   if (!cp_lexer_next_token_is (parser->lexer, CPP_CLOSE_SQUARE))
     while (true)
       {
 	cp_expr e = cp_parser_identifier (parser);
 	if (e.get_value () == error_mark_node)
 	  break;
+	tree attr = NULL_TREE;
+	if (cp_next_tokens_can_be_std_attribute_p (parser))
+	  {
+	    if (cxx_dialect >= cxx17 && cxx_dialect < cxx26 && !attr_diagnosed)
+	      {
+		pedwarn (cp_lexer_peek_token (parser->lexer)->location,
+			 OPT_Wc__26_extensions,
+			 "structured bindings with attributed identifiers "
+			 "only available with %<-std=c++2c%> or "
+			 "%<-std=gnu++2c%>");
+		attr_diagnosed = true;
+	      }
+	    attr = cp_parser_std_attribute_spec_seq (parser);
+	    if (attr == error_mark_node)
+	      attr = NULL_TREE;
+	    if (attr && first_attr == -1)
+	      first_attr = v.length ();
+	  }
 	v.safe_push (e);
+	++cnt;
+	if (first_attr != -1)
+	  v.safe_push (attr);
 	if (!cp_lexer_next_token_is (parser->lexer, CPP_COMMA))
 	  break;
 	cp_lexer_consume_token (parser->lexer);
@@ -16139,8 +16163,11 @@  cp_parser_decomposition_declaration (cp_
 	  declarator->id_loc = e.get_location ();
 	}
       tree elt_pushed_scope;
+      tree attr = NULL_TREE;
+      if (first_attr != -1 && i >= (unsigned) first_attr)
+	attr = v[++i].get_value ();
       tree decl2 = start_decl (declarator, &decl_specs, SD_DECOMPOSITION,
-			       NULL_TREE, NULL_TREE, &elt_pushed_scope);
+			       NULL_TREE, attr, &elt_pushed_scope);
       if (decl2 == error_mark_node)
 	decl = error_mark_node;
       else if (decl != error_mark_node && DECL_CHAIN (decl2) != prev)
@@ -16183,7 +16210,7 @@  cp_parser_decomposition_declaration (cp_
 
       if (decl != error_mark_node)
 	{
-	  cp_decomp decomp = { prev, v.length () };
+	  cp_decomp decomp = { prev, cnt };
 	  cp_finish_decl (decl, initializer, non_constant_p, NULL_TREE,
 			  (is_direct_init ? LOOKUP_NORMAL : LOOKUP_IMPLICIT),
 			  &decomp);
@@ -16193,7 +16220,7 @@  cp_parser_decomposition_declaration (cp_
   else if (decl != error_mark_node)
     {
       *maybe_range_for_decl = prev;
-      cp_decomp decomp = { prev, v.length () };
+      cp_decomp decomp = { prev, cnt };
       /* Ensure DECL_VALUE_EXPR is created for all the decls but
 	 the underlying DECL.  */
       cp_finish_decomp (decl, &decomp);
--- gcc/c-family/c-cppbuiltin.cc.jj	2024-04-26 11:42:24.621016651 +0200
+++ gcc/c-family/c-cppbuiltin.cc	2024-04-26 13:42:50.559184222 +0200
@@ -1044,7 +1044,8 @@  c_cpp_builtins (cpp_reader *pfile)
 	  /* Old macro, superseded by
 	     __cpp_nontype_template_parameter_auto.  */
 	  cpp_define (pfile, "__cpp_template_auto=201606L");
-	  cpp_define (pfile, "__cpp_structured_bindings=201606L");
+	  if (cxx_dialect <= cxx23)
+	    cpp_define (pfile, "__cpp_structured_bindings=201606L");
 	  cpp_define (pfile, "__cpp_variadic_using=201611L");
 	  cpp_define (pfile, "__cpp_guaranteed_copy_elision=201606L");
 	  cpp_define (pfile, "__cpp_nontype_template_parameter_auto=201606L");
@@ -1090,6 +1091,7 @@  c_cpp_builtins (cpp_reader *pfile)
 	  cpp_define (pfile, "__cpp_constexpr=202306L");
 	  cpp_define (pfile, "__cpp_static_assert=202306L");
 	  cpp_define (pfile, "__cpp_placeholder_variables=202306L");
+	  cpp_define (pfile, "__cpp_structured_bindings=202403L");
 	}
       if (flag_concepts)
         {
--- gcc/testsuite/g++.dg/cpp26/decomp1.C.jj	2024-04-26 13:42:50.559184222 +0200
+++ gcc/testsuite/g++.dg/cpp26/decomp1.C	2024-04-26 14:24:59.276090634 +0200
@@ -0,0 +1,33 @@ 
+// { dg-do compile { target c++11 } }
+// { dg-options "" }
+
+namespace std {
+  template<typename T> struct tuple_size;
+  template<int, typename> struct tuple_element;
+}
+
+struct A {
+  int i;
+  template <int I> int& get() { return i; }
+};
+
+template<> struct std::tuple_size<A> { static const int value = 3; };
+template<int I> struct std::tuple_element<I,A> { using type = int; };
+
+struct B {
+  int i, j;
+  long long k, l;
+} z[6];
+
+void
+foo (A &a, B &b)
+{
+  auto [ c [[]], d, e [[maybe_unused]] ] = a;		// { dg-warning "structured bindings with attributed identifiers only available with" "" { target { c++17 && c++23_down } } }
+							// { dg-warning "structured bindings only available with" "" { target c++14_down } .-1 }
+  auto [ f, h [[maybe_unused]] [[]], i [[]], j ] = b;	// { dg-warning "structured bindings with attributed identifiers only available with" "" { target { c++17 && c++23_down } } }
+							// { dg-warning "structured bindings only available with" "" { target c++14_down } .-1 }
+  for (auto [ k, l [[maybe_unused]], m, n [[]]] : z)	// { dg-warning "structured bindings with attributed identifiers only available with" "" { target { c++17 && c++23_down } } }
+    ;							// { dg-warning "structured bindings only available with" "" { target c++14_down } .-1 }
+  auto &[o[[]][[]][[]], p[[]], q[[]]] = a;		// { dg-warning "structured bindings with attributed identifiers only available with" "" { target { c++17 && c++23_down } } }
+    ;							// { dg-warning "structured bindings only available with" "" { target c++14_down } .-1 }
+}
--- gcc/testsuite/g++.dg/cpp26/decomp2.C.jj	2024-04-26 14:21:31.454974172 +0200
+++ gcc/testsuite/g++.dg/cpp26/decomp2.C	2024-04-26 14:28:11.665421202 +0200
@@ -0,0 +1,46 @@ 
+// { dg-do compile { target c++11 } }
+// { dg-options "" }
+
+namespace std {
+  template<typename T> struct tuple_size;
+  template<int, typename> struct tuple_element;
+}
+
+struct A {
+  int i;
+  template <int I> int& get() { return i; }
+};
+
+template<> struct std::tuple_size<A> { static const int value = 3; };
+template<int I> struct std::tuple_element<I,A> { using type = int; };
+
+struct B {
+  int i, j;
+  long long k, l;
+} z[6];
+
+void
+foo (A &a, B &b)
+{
+  auto [ c [[]], d, e [[gnu::deprecated]] ] = a;	// { dg-warning "structured bindings with attributed identifiers only available with" "" { target { c++17 && c++23_down } } }
+							// { dg-warning "structured bindings only available with" "" { target c++14_down } .-1 }
+							// { dg-message "declared here" "" { target *-*-* } .-2 }
+  ++c;
+  ++d;
+  ++e;							// { dg-warning "'e' is deprecated" }
+  auto [ f, h [[gnu::deprecated]] [[]], i [[]], j ] = b;// { dg-warning "structured bindings with attributed identifiers only available with" "" { target { c++17 && c++23_down } } }
+							// { dg-warning "structured bindings only available with" "" { target c++14_down } .-1 }
+							// { dg-message "declared here" "" { target *-*-* } .-2 }
+  ++f;
+  ++h;							// { dg-warning "'h' is deprecated" }
+  ++i;
+  ++j;
+  for (auto [ k, l [[gnu::deprecated]], m, n [[]]] : z)	// { dg-warning "structured bindings with attributed identifiers only available with" "" { target { c++17 && c++23_down } } }
+    {							// { dg-warning "structured bindings only available with" "" { target c++14_down } .-1 }
+							// { dg-message "declared here" "" { target *-*-* } .-2 }
+      ++k;
+      ++l;						// { dg-warning "'l' is deprecated" }
+      ++m;
+      ++n;
+    }
+}
--- gcc/testsuite/g++.dg/cpp26/feat-cxx26.C.jj	2024-04-26 11:42:24.675015903 +0200
+++ gcc/testsuite/g++.dg/cpp26/feat-cxx26.C	2024-04-26 13:42:50.559184222 +0200
@@ -394,8 +394,8 @@ 
 
 #ifndef __cpp_structured_bindings
 #  error "__cpp_structured_bindings"
-#elif __cpp_structured_bindings != 201606
-#  error "__cpp_structured_bindings != 201606"
+#elif __cpp_structured_bindings != 202403
+#  error "__cpp_structured_bindings != 202403"
 #endif
 
 #ifndef __cpp_template_template_args