&& 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:
{
/* 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. */
--- /dev/null
+// 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;