Message ID | Zi9IPOngOU/UWePb@tucnak |
---|---|
State | New |
Headers | show |
Series | c++: Implement C++26 P0609R3 - Attributes for Structured Bindings [PR114456] | expand |
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 >
--- 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