aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorJakub Jelinek <jakub@redhat.com>2024-05-07 22:38:01 +0200
committerJakub Jelinek <jakub@redhat.com>2024-05-07 22:38:01 +0200
commit17458d2bc74b904968e6bdc12527eb040c8d2370 (patch)
tree8b04d926081608e70d1255dd7b4e7f803ad19ee4 /gcc
parent28ee13db2e9d995bd3728c4ff3a3545e24b39cd2 (diff)
downloadgcc-17458d2bc74b904968e6bdc12527eb040c8d2370.zip
gcc-17458d2bc74b904968e6bdc12527eb040c8d2370.tar.gz
gcc-17458d2bc74b904968e6bdc12527eb040c8d2370.tar.bz2
c++: Implement C++26 P2893R3 - Variadic friends [PR114459]
The following patch imeplements the C++26 P2893R3 - Variadic friends paper. The paper allows for the friend type declarations to specify more than one friend type specifier and allows to specify ... at the end of each. The patch doesn't introduce tentative parsing of friend-type-declaration non-terminal, but rather just extends existing parsing where it is a friend declaration which ends with ; after the declaration specifiers to the cases where it ends with ...; or , or ..., In that case it pedwarns for cxx_dialect < cxx26, handles the ... and if there is , continues in a loop to parse the further friend type specifiers. 2024-05-07 Jakub Jelinek <jakub@redhat.com> PR c++/114459 gcc/c-family/ * c-cppbuiltin.cc (c_cpp_builtins): Predefine __cpp_variadic_friend=202403L for C++26. gcc/cp/ * parser.cc (cp_parser_member_declaration): Implement C++26 P2893R3 - Variadic friends. Parse friend type declarations with ... or with more than one friend type specifier. * friend.cc (make_friend_class): Allow TYPE_PACK_EXPANSION. * pt.cc (instantiate_class_template): Handle PACK_EXPANSION_P in friend classes. gcc/testsuite/ * g++.dg/cpp26/feat-cxx26.C (__cpp_variadic_friend): Add test. * g++.dg/cpp26/variadic-friend1.C: New test.
Diffstat (limited to 'gcc')
-rw-r--r--gcc/c-family/c-cppbuiltin.cc1
-rw-r--r--gcc/cp/friend.cc3
-rw-r--r--gcc/cp/parser.cc126
-rw-r--r--gcc/cp/pt.cc16
-rw-r--r--gcc/testsuite/g++.dg/cpp26/feat-cxx26.C6
-rw-r--r--gcc/testsuite/g++.dg/cpp26/variadic-friend1.C58
6 files changed, 169 insertions, 41 deletions
diff --git a/gcc/c-family/c-cppbuiltin.cc b/gcc/c-family/c-cppbuiltin.cc
index b6f25e4..d9b84a0 100644
--- a/gcc/c-family/c-cppbuiltin.cc
+++ b/gcc/c-family/c-cppbuiltin.cc
@@ -1093,6 +1093,7 @@ c_cpp_builtins (cpp_reader *pfile)
cpp_define (pfile, "__cpp_placeholder_variables=202306L");
cpp_define (pfile, "__cpp_structured_bindings=202403L");
cpp_define (pfile, "__cpp_deleted_function=202403L");
+ cpp_define (pfile, "__cpp_variadic_friend=202403L");
}
if (flag_concepts)
{
diff --git a/gcc/cp/friend.cc b/gcc/cp/friend.cc
index 758ea87..2e70d01 100644
--- a/gcc/cp/friend.cc
+++ b/gcc/cp/friend.cc
@@ -279,7 +279,8 @@ make_friend_class (tree type, tree friend_type, bool complain)
}
if (! MAYBE_CLASS_TYPE_P (friend_type)
- && TREE_CODE (friend_type) != TEMPLATE_TEMPLATE_PARM)
+ && TREE_CODE (friend_type) != TEMPLATE_TEMPLATE_PARM
+ && TREE_CODE (friend_type) != TYPE_PACK_EXPANSION)
{
/* N1791: If the type specifier in a friend declaration designates a
(possibly cv-qualified) class type, that class is declared as a
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index 775067e..c419120 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -28100,8 +28100,17 @@ cp_parser_member_declaration (cp_parser* parser)
&& cp_parser_parse_and_diagnose_invalid_type_name (parser))
goto out;
/* If there is no declarator, then the decl-specifier-seq should
- specify a type. */
- if (cp_lexer_next_token_is (parser->lexer, CPP_SEMICOLON))
+ specify a type. For C++26 Variadic friends don't just check for
+ a semicolon, but also for a comma and in both cases optionally
+ preceded by ellipsis. */
+ if (cp_lexer_next_token_is (parser->lexer, CPP_SEMICOLON)
+ || (cp_parser_friend_p (&decl_specifiers)
+ && cxx_dialect >= cxx11
+ && (cp_lexer_next_token_is (parser->lexer, CPP_COMMA)
+ || (cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS)
+ && (cp_lexer_nth_token_is (parser->lexer, 2, CPP_SEMICOLON)
+ || cp_lexer_nth_token_is (parser->lexer, 2,
+ CPP_COMMA))))))
{
/* If there was no decl-specifier-seq, and the next token is a
`;', then we have something like:
@@ -28136,44 +28145,81 @@ cp_parser_member_declaration (cp_parser* parser)
{
/* If the `friend' keyword was present, the friend must
be introduced with a class-key. */
- if (!declares_class_or_enum && cxx_dialect < cxx11)
- pedwarn (decl_spec_token_start->location, OPT_Wpedantic,
- "in C++03 a class-key must be used "
- "when declaring a friend");
- /* In this case:
-
- template <typename T> struct A {
- friend struct A<T>::B;
- };
-
- A<T>::B will be represented by a TYPENAME_TYPE, and
- therefore not recognized by check_tag_decl. */
- if (!type)
- {
- type = decl_specifiers.type;
- if (type && TREE_CODE (type) == TYPE_DECL)
- type = TREE_TYPE (type);
- }
- /* Warn if an attribute cannot appear here, as per
- [dcl.attr.grammar]/5. But not when declares_class_or_enum:
- we ignore attributes in elaborated-type-specifiers. */
- if (!declares_class_or_enum
- && cxx11_attribute_p (decl_specifiers.attributes))
- {
- decl_specifiers.attributes = NULL_TREE;
- if (warning_at (decl_spec_token_start->location,
- OPT_Wattributes, "attribute ignored"))
- inform (decl_spec_token_start->location, "an attribute "
- "that appertains to a friend declaration that "
- "is not a definition is ignored");
- }
- if (!type || !TYPE_P (type))
- error_at (decl_spec_token_start->location,
- "friend declaration does not name a class or "
- "function");
- else
- make_friend_class (current_class_type, type,
- /*complain=*/true);
+ if (!declares_class_or_enum && cxx_dialect < cxx11)
+ pedwarn (decl_spec_token_start->location, OPT_Wpedantic,
+ "in C++03 a class-key must be used "
+ "when declaring a friend");
+ if (!cp_lexer_next_token_is (parser->lexer, CPP_SEMICOLON)
+ && cxx_dialect < cxx26)
+ pedwarn (cp_lexer_peek_token (parser->lexer)->location,
+ OPT_Wc__26_extensions,
+ "variadic friends or friend type declarations with "
+ "multiple types only available with "
+ "%<-std=c++2c%> or %<-std=gnu++2c%>");
+ location_t friend_loc = decl_specifiers.locations[ds_friend];
+ do
+ {
+ /* In this case:
+
+ template <typename T> struct A {
+ friend struct A<T>::B;
+ };
+
+ A<T>::B will be represented by a TYPENAME_TYPE, and
+ therefore not recognized by check_tag_decl. */
+ if (!type)
+ {
+ type = decl_specifiers.type;
+ if (type && TREE_CODE (type) == TYPE_DECL)
+ type = TREE_TYPE (type);
+ }
+ /* Warn if an attribute cannot appear here, as per
+ [dcl.attr.grammar]/5. But not when
+ declares_class_or_enum: we ignore attributes in
+ elaborated-type-specifiers. */
+ if (!declares_class_or_enum
+ && cxx11_attribute_p (decl_specifiers.attributes))
+ {
+ decl_specifiers.attributes = NULL_TREE;
+ if (warning_at (decl_spec_token_start->location,
+ OPT_Wattributes, "attribute ignored"))
+ inform (decl_spec_token_start->location, "an attribute "
+ "that appertains to a friend declaration that "
+ "is not a definition is ignored");
+ }
+ bool ellipsis = cp_lexer_next_token_is (parser->lexer,
+ CPP_ELLIPSIS);
+ if (ellipsis)
+ cp_lexer_consume_token (parser->lexer);
+ if (!type || !TYPE_P (type))
+ error_at (decl_spec_token_start->location,
+ "friend declaration does not name a class or "
+ "function");
+ else
+ {
+ if (ellipsis)
+ type = make_pack_expansion (type);
+ if (type != error_mark_node)
+ make_friend_class (current_class_type, type,
+ /*complain=*/true);
+ }
+ if (!cp_lexer_next_token_is (parser->lexer, CPP_COMMA))
+ break;
+ cp_lexer_consume_token (parser->lexer);
+ clear_decl_specs (&decl_specifiers);
+ decl_specifiers.locations[ds_friend] = friend_loc;
+ decl_specifiers.any_specifiers_p = true;
+ declares_class_or_enum = false;
+ cp_parser_type_specifier (parser,
+ CP_PARSER_FLAGS_TYPENAME_OPTIONAL,
+ &decl_specifiers,
+ /*is_declaration=*/true,
+ &declares_class_or_enum, NULL);
+ type = check_tag_decl (&decl_specifiers,
+ /*explicit_type_instantiation_p=*/
+ false);
+ }
+ while (1);
}
/* If there is no TYPE, an error message will already have
been issued. */
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index 7a3a4e7..1816bfd 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -12693,6 +12693,22 @@ instantiate_class_template (tree type)
tf_warning_or_error, NULL_TREE);
--processing_template_decl;
}
+ else if (PACK_EXPANSION_P (friend_type))
+ {
+ friend_type = tsubst_pack_expansion (friend_type, args,
+ tf_warning_or_error,
+ NULL_TREE);
+ if (friend_type != error_mark_node)
+ {
+ unsigned int len = TREE_VEC_LENGTH (friend_type);
+ for (unsigned int idx = 0; idx < len; ++idx)
+ if (TREE_VEC_ELT (friend_type, idx) != error_mark_node)
+ make_friend_class (type,
+ TREE_VEC_ELT (friend_type, idx),
+ /*complain=*/false);
+ }
+ friend_type = error_mark_node;
+ }
else if (uses_template_parms (friend_type))
/* friend class C<T>; */
friend_type = tsubst (friend_type, args,
diff --git a/gcc/testsuite/g++.dg/cpp26/feat-cxx26.C b/gcc/testsuite/g++.dg/cpp26/feat-cxx26.C
index de66dcc..8a5fd64 100644
--- a/gcc/testsuite/g++.dg/cpp26/feat-cxx26.C
+++ b/gcc/testsuite/g++.dg/cpp26/feat-cxx26.C
@@ -615,3 +615,9 @@
#elif __cpp_deleted_function != 202403
# error "__cpp_deleted_function != 202403"
#endif
+
+#ifndef __cpp_variadic_friend
+# error "__cpp_variadic_friend"
+#elif __cpp_variadic_friend != 202403
+# error "__cpp_variadic_friend != 202403"
+#endif
diff --git a/gcc/testsuite/g++.dg/cpp26/variadic-friend1.C b/gcc/testsuite/g++.dg/cpp26/variadic-friend1.C
new file mode 100644
index 0000000..25c6c45
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp26/variadic-friend1.C
@@ -0,0 +1,58 @@
+// P2893R3 - Variadic friends
+// { dg-do compile { target c++11 } }
+// { dg-options "" }
+
+template <class... Ts>
+class A {
+ class X {};
+ friend Ts...; // { dg-warning "variadic friends or friend type declarations with multiple types only available with" "" { target c++23_down } }
+};
+template <class... Ts, class... Us>
+class A<A<Ts...>, A<Us...>> {
+ class X {};
+ friend
+#if __cplusplus < 202002L
+ typename
+#endif
+ Ts::Y..., Us...; // { dg-warning "variadic friends or friend type declarations with multiple types only available with" "" { target c++23_down } }
+};
+template <typename T, typename U>
+class B {
+ class X {};
+ friend T, U; // { dg-warning "variadic friends or friend type declarations with multiple types only available with" "" { target c++23_down } }
+};
+template <typename T, typename U, typename... Vs>
+class C {
+ class X {};
+ friend U, Vs..., T; // { dg-warning "variadic friends or friend type declarations with multiple types only available with" "" { target c++23_down } }
+};
+class E;
+class F;
+class G;
+class H;
+class I;
+class J;
+class K;
+class L;
+class M;
+class N;
+class O;
+class P;
+class E : A<E, F>::X {};
+class F : A<E, F>::X {};
+class G : B<G, H>::X {};
+class H : B<G, H>::X {};
+class I : C<I, J>::X {};
+class J : C<I, J>::X {};
+class K : C<K, L, M, N, O>::X {};
+class L : C<K, L, M, N, O>::X {};
+class M : C<K, L, M, N, O>::X {};
+class N : C<K, L, M, N, O>::X {};
+class O : C<K, L, M, N, O>::X {};
+struct Q { class Y : A<A<Q>, A<P, long>>::X {}; };
+class P : A<A<Q>, A<P, long>>::X {};
+struct R { class Y; };
+struct S { class Y; };
+class R::Y : A<A<R, S>, A<P, double>>::X {};
+class S::Y : A<A<R, S>, A<P, double>>::X {};
+A<int> a;