- if (cp_lexer_next_token_is (parser->lexer, CPP_SEMICOLON))
+ 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:
@@ -28137,44 +28144,81 @@ cp_parser_member_declaration (cp_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:
+ 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;
- };
+ 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);
+ 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. */
--- gcc/cp/friend.cc.jj 2024-01-03 12:01:23.136483926 +0100
+++ gcc/cp/friend.cc 2024-05-03 13:34:25.182765869 +0200
@@ -279,7 +279,8 @@ make_friend_class (tree type, tree frien
}
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
--- gcc/cp/pt.cc.jj 2024-05-03 09:43:47.813511040 +0200
+++ gcc/cp/pt.cc 2024-05-03 13:56:48.363582051 +0200
@@ -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,
--- gcc/testsuite/g++.dg/cpp26/feat-cxx26.C.jj 2024-05-02 09:31:17.754298166
+0200
+++ gcc/testsuite/g++.dg/cpp26/feat-cxx26.C 2024-05-03 14:49:10.432023113
+0200
@@ -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
--- gcc/testsuite/g++.dg/cpp26/variadic-friend1.C.jj 2024-05-03
14:46:53.887873294 +0200
+++ gcc/testsuite/g++.dg/cpp26/variadic-friend1.C 2024-05-03
14:45:50.624730486 +0200
@@ -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;
Jakub