mirror of
https://github.com/gcc-mirror/gcc.git
synced 2024-11-21 13:40:47 +00:00
c++: Implement C++26 P0609R3 - Attributes for Structured Bindings [PR114456]
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. 2024-04-30 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.
This commit is contained in:
parent
4d3a5618de
commit
bd35a92f8d
@ -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)
|
||||
{
|
||||
|
@ -16075,13 +16075,37 @@ cp_parser_decomposition_declaration (cp_parser *parser,
|
||||
|
||||
/* 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_parser *parser,
|
||||
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_parser *parser,
|
||||
|
||||
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_parser *parser,
|
||||
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);
|
||||
|
33
gcc/testsuite/g++.dg/cpp26/decomp1.C
Normal file
33
gcc/testsuite/g++.dg/cpp26/decomp1.C
Normal file
@ -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 }
|
||||
}
|
46
gcc/testsuite/g++.dg/cpp26/decomp2.C
Normal file
46
gcc/testsuite/g++.dg/cpp26/decomp2.C
Normal file
@ -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;
|
||||
}
|
||||
}
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user