public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
From: Jason Merrill <jason@redhat.com>
To: Jakub Jelinek <jakub@redhat.com>
Cc: gcc-patches@gcc.gnu.org
Subject: Re: [PATCH] c++: Implement C++26 P2893R3 - Variadic friends [PR114459]
Date: Tue, 7 May 2024 16:27:11 -0400	[thread overview]
Message-ID: <04cc28cf-f732-41d0-a3ed-9132c8ac7504@redhat.com> (raw)
In-Reply-To: <ZjUSTFY+YKS6dYL0@tucnak>

On 5/3/24 12:35, Jakub Jelinek wrote:
> Hi!
> 
> 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.
> 
> Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?
> 
> 2024-05-03  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.
> 
> --- gcc/c-family/c-cppbuiltin.cc.jj	2024-05-02 09:31:17.746298275 +0200
> +++ gcc/c-family/c-cppbuiltin.cc	2024-05-03 14:50:08.008242950 +0200
> @@ -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)
>           {
> --- gcc/cp/parser.cc.jj	2024-05-03 09:43:47.781511477 +0200
> +++ gcc/cp/parser.cc	2024-05-03 13:26:38.208088017 +0200
> @@ -28102,7 +28102,14 @@ cp_parser_member_declaration (cp_parser*
>       goto out;
>     /* If there is no declarator, then the decl-specifier-seq should
>        specify a type.  */

Let's mention C++26 variadic friends in this comment.  OK with that change.

> -  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
> 


      reply	other threads:[~2024-05-07 20:27 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-05-03 16:35 Jakub Jelinek
2024-05-07 20:27 ` Jason Merrill [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=04cc28cf-f732-41d0-a3ed-9132c8ac7504@redhat.com \
    --to=jason@redhat.com \
    --cc=gcc-patches@gcc.gnu.org \
    --cc=jakub@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).