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++23 P1169R4 - static operator() [PR106651]
Date: Sat, 17 Sep 2022 01:23:59 +0200	[thread overview]
Message-ID: <35c635ad-118f-63bd-eb58-cd949286e28d@redhat.com> (raw)
In-Reply-To: <YyCy9OxAaLBDi+1V@tucnak>

On 9/13/22 12:42, Jakub Jelinek wrote:
> Hi!
> 
> The following patch attempts to implement C++23 P1169R4 - static operator()
> paper's compiler side (there is some small library side too not implemented
> yet).  This allows static members as user operator() declarations and
> static specifier on lambdas without lambda capture.  As decl specifier
> parsing doesn't track about the presence and locations of all specifiers,
> the patch unfortunately replaces the diagnostics about duplicate mutable
> with diagnostics about conflicting specifiers because the information
> whether it was mutable mutable, mutable static, static mutable or static
> static is lost.

I wonder why we don't give an error when setting the 
conflicting_specifiers_p flag in cp_parser_set_storage_class?  We should 
be able to give a better diagnostic at that point.

> Beyond this, the synthetized conversion operator changes
> for static lambdas as it can just return the operator() static method
> address, doesn't need to create a thunk for it.
> The change I'm least sure about is the call.cc (joust) change, one thing
> is that we ICEd because we assumed that len could be different only if
> both candidates are direct calls but it can be one direct and one indirect
> call,

How do you mean?

> and then I'm trying to implement my understanding of the
> [over.match.best.general]/1 and [over.best.ics.general] changes from
> the paper (implemented both for C++23 and when the static member function
> is operator() which we accept with pedwarn in earlier standards too).
> 
> Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?
> 
> 2022-09-13  Jakub Jelinek  <jakub@redhat.com>
> 
> 	PR c++/106651
> gcc/c-family/
> 	* c-cppbuiltin.cc (c_cpp_builtins): Predefine
> 	__cpp_static_call_operator=202207L for C++23.
> gcc/cp/
> 	* cp-tree.h (LAMBDA_EXPR_STATIC_P): Implement C++23
> 	P1169R4 - static operator().  Define.
> 	* parser.cc (CP_PARSER_FLAGS_ONLY_MUTABLE_OR_CONSTEXPR): Document
> 	that it also allows static.
> 	(cp_parser_lambda_declarator_opt): Handle static lambda specifier.
> 	(cp_parser_decl_specifier_seq): Allow RID_STATIC for
> 	CP_PARSER_FLAGS_ONLY_MUTABLE_OR_CONSTEXPR.
> 	* decl.cc (grok_op_properties): If operator() isn't a method,
> 	use a different error wording, if it is static member function,
> 	allow it (for C++20 and older with a pedwarn unless it is
> 	a lambda function or template instantiation).
> 	* call.cc (joust): Don't ICE if one candidate is static member
> 	function and the other is an indirect call.  For C++23 or if
> 	the static member is operator() and the parameter conversion on
> 	the other candidate is user defined conversion, ellipsis or bad
> 	conversion, make static member function candidate a winner for
> 	that parameter.
> 	* lambda.cc (maybe_add_lambda_conv_op): Handle static lambdas.
> 	* error.cc (dump_lambda_function): Print static for static lambdas.
> gcc/testsuite/
> 	* g++.dg/template/error30.C: Adjust expected diagnostics.
> 	* g++.dg/cpp1z/constexpr-lambda13.C: Likewise.
> 	* g++.dg/cpp23/feat-cxx2b.C: Test __cpp_static_call_operator.
> 	* g++.dg/cpp23/static-operator-call1.C: New test.
> 	* g++.dg/cpp23/static-operator-call2.C: New test.
> 	* g++.old-deja/g++.jason/operator.C: Adjust expected diagnostics.
> 
> --- gcc/cp/cp-tree.h.jj	2022-09-13 09:21:28.052541628 +0200
> +++ gcc/cp/cp-tree.h	2022-09-13 12:14:31.674733861 +0200
> @@ -504,6 +504,7 @@ extern GTY(()) tree cp_global_trees[CPTI
>         OVL_NESTED_P (in OVERLOAD)
>         DECL_MODULE_EXPORT_P (in _DECL)
>         PACK_EXPANSION_FORCE_EXTRA_ARGS_P (in *_PACK_EXPANSION)
> +      LAMBDA_EXPR_STATIC_P (in LAMBDA_EXPR)
>      4: IDENTIFIER_MARKED (IDENTIFIER_NODEs)
>         TREE_HAS_CONSTRUCTOR (in INDIRECT_REF, SAVE_EXPR, CONSTRUCTOR,
>   	  CALL_EXPR, or FIELD_DECL).
> @@ -1488,6 +1489,10 @@ enum cp_lambda_default_capture_mode_type
>   #define LAMBDA_EXPR_CAPTURE_OPTIMIZED(NODE) \
>     TREE_LANG_FLAG_2 (LAMBDA_EXPR_CHECK (NODE))
>   
> +/* Predicate tracking whether the lambda was declared 'static'.  */
> +#define LAMBDA_EXPR_STATIC_P(NODE) \
> +  TREE_LANG_FLAG_3 (LAMBDA_EXPR_CHECK (NODE))
> +
>   /* True if this TREE_LIST in LAMBDA_EXPR_CAPTURE_LIST is for an explicit
>      capture.  */
>   #define LAMBDA_CAPTURE_EXPLICIT_P(NODE) \
> --- gcc/cp/parser.cc.jj	2022-09-13 09:21:01.276920558 +0200
> +++ gcc/cp/parser.cc	2022-09-13 12:14:31.683733738 +0200
> @@ -1994,7 +1994,7 @@ enum
>        constexpr.  */
>     CP_PARSER_FLAGS_ONLY_TYPE_OR_CONSTEXPR = 0x8,
>     /* When parsing a decl-specifier-seq, only allow mutable, constexpr or
> -     for C++20 consteval.  */
> +     for C++20 consteval or for C++23 static.  */
>     CP_PARSER_FLAGS_ONLY_MUTABLE_OR_CONSTEXPR = 0x10,
>     /* When parsing a decl-specifier-seq, allow missing typename.  */
>     CP_PARSER_FLAGS_TYPENAME_OPTIONAL = 0x20,
> @@ -11714,13 +11714,26 @@ cp_parser_lambda_declarator_opt (cp_pars
>         omitted_parms_loc = UNKNOWN_LOCATION;
>       }
>   
> -  if (lambda_specs.storage_class == sc_mutable)
> +  if (lambda_specs.storage_class == sc_mutable
> +      || lambda_specs.storage_class == sc_static)
>       {
> -      LAMBDA_EXPR_MUTABLE_P (lambda_expr) = 1;
> -      quals = TYPE_UNQUALIFIED;
> +      if (lambda_specs.storage_class == sc_mutable)
> +	{
> +	  LAMBDA_EXPR_MUTABLE_P (lambda_expr) = 1;
> +	  quals = TYPE_UNQUALIFIED;
> +	}
> +      else if (LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (lambda_expr) != CPLD_NONE
> +	       || LAMBDA_EXPR_CAPTURE_LIST (lambda_expr))
> +	error_at (lambda_specs.locations[ds_storage_class],
> +		  "%<static%> lambda specifier with lambda capture");
> +      else
> +	{
> +	  LAMBDA_EXPR_STATIC_P (lambda_expr) = 1;
> +	  quals = TYPE_UNQUALIFIED;
> +	}
>         if (lambda_specs.conflicting_specifiers_p)
>   	error_at (lambda_specs.locations[ds_storage_class],
> -		  "duplicate %<mutable%>");
> +		  "conflicting lambda specifiers");
>       }
>   
>     tx_qual = cp_parser_tx_qualifier_opt (parser);
> @@ -11807,6 +11820,12 @@ cp_parser_lambda_declarator_opt (cp_pars
>       if (lambda_specs.locations[ds_consteval])
>         return_type_specs.locations[ds_consteval]
>   	= lambda_specs.locations[ds_consteval];
> +    if (LAMBDA_EXPR_STATIC_P (lambda_expr))
> +      {
> +	return_type_specs.storage_class = sc_static;
> +	return_type_specs.locations[ds_storage_class]
> +	  = lambda_specs.locations[ds_storage_class];
> +      }
>   
>       p = obstack_alloc (&declarator_obstack, 0);
>   
> @@ -11830,8 +11849,9 @@ cp_parser_lambda_declarator_opt (cp_pars
>         {
>   	DECL_INITIALIZED_IN_CLASS_P (fco) = 1;
>   	DECL_ARTIFICIAL (fco) = 1;
> -	/* Give the object parameter a different name.  */
> -	DECL_NAME (DECL_ARGUMENTS (fco)) = closure_identifier;
> +	if (!LAMBDA_EXPR_STATIC_P (lambda_expr))
> +	  /* Give the object parameter a different name.  */
> +	  DECL_NAME (DECL_ARGUMENTS (fco)) = closure_identifier;
>   	DECL_SET_LAMBDA_FUNCTION (fco, true);
>         }
>       if (template_param_list)
> @@ -16008,8 +16028,15 @@ cp_parser_decl_specifier_seq (cp_parser*
>   	  && token->keyword != RID_MUTABLE
>   	  && token->keyword != RID_CONSTEXPR
>   	  && token->keyword != RID_CONSTEVAL)
> -	error_at (token->location, "%qD invalid in lambda",
> -		  ridpointers[token->keyword]);
> +	{
> +	  if (token->keyword != RID_STATIC)
> +	    error_at (token->location, "%qD invalid in lambda",
> +		      ridpointers[token->keyword]);
> +	  else if (cxx_dialect < cxx23)
> +	    pedwarn (token->location, OPT_Wc__23_extensions,
> +		     "%qD only valid in lambda with %<-std=c++23%> or "
> +		     "%<-std=gnu++23%>", ridpointers[token->keyword]);
> +	}
>   
>         if (ds != ds_last)
>   	set_and_check_decl_spec_loc (decl_specs, ds, token);
> --- gcc/cp/decl.cc.jj	2022-09-13 09:21:28.062541487 +0200
> +++ gcc/cp/decl.cc	2022-09-13 12:14:31.677733820 +0200
> @@ -15299,8 +15299,25 @@ grok_op_properties (tree decl, bool comp
>        an enumeration, or a reference to an enumeration.  13.4.0.6 */
>     if (! methodp || DECL_STATIC_FUNCTION_P (decl))
>       {
> +      if (operator_code == CALL_EXPR)
> +	{
> +	  if (! DECL_STATIC_FUNCTION_P (decl))
> +	    {
> +	      error_at (loc, "%qD must be a member function", decl);
> +	      return false;
> +	    }
> +	  if (cxx_dialect < cxx23
> +	      /* For lambdas we diagnose static lambda specifier elsewhere.  */
> +	      && ! LAMBDA_FUNCTION_P (decl)
> +	      /* For instantiations, we have diagnosed this already.  */
> +	      && ! DECL_USE_TEMPLATE (decl))
> +	    pedwarn (loc, OPT_Wc__23_extensions, "%qD may be a static member "
> +	      "function only with %<-std=c++23%> or %<-std=gnu++23%>", decl);
> +	  /* There are no further restrictions on the arguments to an
> +	     overloaded "operator ()".  */
> +	  return true;
> +	}
>         if (operator_code == TYPE_EXPR
> -	  || operator_code == CALL_EXPR
>   	  || operator_code == COMPONENT_REF
>   	  || operator_code == ARRAY_REF
>   	  || operator_code == NOP_EXPR)
> --- gcc/cp/call.cc.jj	2022-09-13 09:21:28.008542251 +0200
> +++ gcc/cp/call.cc	2022-09-13 12:34:10.307510932 +0200
> @@ -12133,10 +12133,14 @@ joust (struct z_candidate *cand1, struct
>     len = cand1->num_convs;
>     if (len != cand2->num_convs)
>       {
> -      int static_1 = DECL_STATIC_FUNCTION_P (cand1->fn);
> -      int static_2 = DECL_STATIC_FUNCTION_P (cand2->fn);
> +      int static_1 = (TREE_CODE (cand1->fn) == FUNCTION_DECL
> +		      && DECL_STATIC_FUNCTION_P (cand1->fn));
> +      int static_2 = (TREE_CODE (cand2->fn) == FUNCTION_DECL
> +		      && DECL_STATIC_FUNCTION_P (cand2->fn));
>   
> -      if (DECL_CONSTRUCTOR_P (cand1->fn)
> +      if (TREE_CODE (cand1->fn) == FUNCTION_DECL
> +	  && TREE_CODE (cand2->fn) == FUNCTION_DECL
> +	  && DECL_CONSTRUCTOR_P (cand1->fn)
>   	  && is_list_ctor (cand1->fn) != is_list_ctor (cand2->fn))
>   	/* We're comparing a near-match list constructor and a near-match
>   	   non-list constructor.  Just treat them as unordered.  */
> @@ -12145,9 +12149,29 @@ joust (struct z_candidate *cand1, struct
>         gcc_assert (static_1 != static_2);
>   
>         if (static_1)
> -	off2 = 1;
> +	{
> +	  /* C++23 [over.best.ics.general] says:
> +	     When the parameter is the implicit object parameter of a static
> +	     member function, the implicit conversion sequence is a standard
> +	     conversion sequence that is neither better nor worse than any
> +	     other standard conversion sequence.
> +	     Apply this for C++23 or when the static member function is
> +	     overloaded call operator (C++23 feature we accept as an
> +	     extension).  */
> +	  if ((cxx_dialect >= cxx23
> +	       || (DECL_OVERLOADED_OPERATOR_P (cand1->fn)
> +		   && DECL_OVERLOADED_OPERATOR_IS (cand1->fn, CALL_EXPR)))
> +	      && CONVERSION_RANK (cand2->convs[0]) >= cr_user)
> +	    winner = -1;

I don't know what you're trying to do here.  The above passage describes 
how we've always compared static to non-static, which should work the 
same way for operator().

> +	  off2 = 1;
> +	}
>         else
>   	{
> +	  if ((cxx_dialect >= cxx23
> +	       || (DECL_OVERLOADED_OPERATOR_P (cand2->fn)
> +		   && DECL_OVERLOADED_OPERATOR_IS (cand2->fn, CALL_EXPR)))
> +	      && CONVERSION_RANK (cand1->convs[0]) >= cr_user)
> +	    winner = -1;
>   	  off1 = 1;
>   	  --len;
>   	}
> --- gcc/cp/lambda.cc.jj	2022-09-13 09:21:28.119540680 +0200
> +++ gcc/cp/lambda.cc	2022-09-13 12:14:31.674733861 +0200
> @@ -1099,7 +1099,9 @@ maybe_add_lambda_conv_op (tree type)
>     tree optype = TREE_TYPE (callop);
>     tree fn_result = TREE_TYPE (optype);
>   
> -  tree thisarg = build_int_cst (TREE_TYPE (DECL_ARGUMENTS (callop)), 0);
> +  tree thisarg = NULL_TREE;
> +  if (TREE_CODE (optype) == METHOD_TYPE)
> +    thisarg = build_int_cst (TREE_TYPE (DECL_ARGUMENTS (callop)), 0);
>     if (generic_lambda_p)
>       {
>         ++processing_template_decl;
> @@ -1109,18 +1111,22 @@ maybe_add_lambda_conv_op (tree type)
>   	 return expression for a deduced return call op to allow for simple
>   	 implementation of the conversion operator.  */
>   
> -      tree instance = cp_build_fold_indirect_ref (thisarg);
>         tree objfn = lookup_template_function (DECL_NAME (callop),
>   					     DECL_TI_ARGS (callop));
> -      objfn = build_min (COMPONENT_REF, NULL_TREE,
> -			 instance, objfn, NULL_TREE);
> -      int nargs = list_length (DECL_ARGUMENTS (callop)) - 1;
> +      int nargs = list_length (DECL_ARGUMENTS (callop));
> +      if (thisarg)
> +	{
> +	  tree instance = cp_build_fold_indirect_ref (thisarg);
> +	  objfn = build_min (COMPONENT_REF, NULL_TREE,
> +			     instance, objfn, NULL_TREE);
> +	  --nargs;
> +	  call = prepare_op_call (objfn, nargs);
> +	}
>   
> -      call = prepare_op_call (objfn, nargs);
>         if (type_uses_auto (fn_result))
>   	decltype_call = prepare_op_call (objfn, nargs);
>       }
> -  else
> +  else if (thisarg)
>       {
>         direct_argvec = make_tree_vector ();
>         direct_argvec->quick_push (thisarg);
> @@ -1135,9 +1141,13 @@ maybe_add_lambda_conv_op (tree type)
>     tree fn_args = NULL_TREE;
>     {
>       int ix = 0;
> -    tree src = DECL_CHAIN (DECL_ARGUMENTS (callop));
> +    tree src = DECL_ARGUMENTS (callop);
>       tree tgt = NULL;
>   
> +    if (thisarg)
> +      src = DECL_CHAIN (src);

You could use FUNCTION_FIRST_USER_PARM to skip 'this' if present.

> +    else if (!decltype_call)
> +      src = NULL_TREE;
>       while (src)
>         {
>   	tree new_node = copy_node (src);
> @@ -1160,12 +1170,15 @@ maybe_add_lambda_conv_op (tree type)
>   	if (generic_lambda_p)
>   	  {
>   	    tree a = tgt;
> -	    if (DECL_PACK_P (tgt))
> +	    if (thisarg)

This is wrong, pack expansion handling has nothing to do with 'this'. 
Only the assignment to CALL_EXPR_ARG should depend on thisarg.

>   	      {
> -		a = make_pack_expansion (a);
> -		PACK_EXPANSION_LOCAL_P (a) = true;
> +		if (DECL_PACK_P (tgt))
> +		  {
> +		    a = make_pack_expansion (a);
> +		    PACK_EXPANSION_LOCAL_P (a) = true;
> +		  }
> +		CALL_EXPR_ARG (call, ix) = a;
>   	      }
> -	    CALL_EXPR_ARG (call, ix) = a;
>   
>   	    if (decltype_call)
>   	      {
> @@ -1193,7 +1206,7 @@ maybe_add_lambda_conv_op (tree type)
>   	     tf_warning_or_error);
>   	}
>       }
> -  else
> +  else if (thisarg)
>       {
>         /* Don't warn on deprecated or unavailable lambda declarations, unless
>   	 the lambda is actually called.  */
> @@ -1203,14 +1216,18 @@ maybe_add_lambda_conv_op (tree type)
>   			   direct_argvec->address ());
>       }
>   
> -  CALL_FROM_THUNK_P (call) = 1;
> -  SET_EXPR_LOCATION (call, UNKNOWN_LOCATION);
> +  if (thisarg)
> +    {
> +      CALL_FROM_THUNK_P (call) = 1;
> +      SET_EXPR_LOCATION (call, UNKNOWN_LOCATION);
> +    }
>   
> -  tree stattype = build_function_type (fn_result, FUNCTION_ARG_CHAIN (callop));
> -  stattype = (cp_build_type_attribute_variant
> -	      (stattype, TYPE_ATTRIBUTES (optype)));
> -  if (flag_noexcept_type
> -      && TYPE_NOTHROW_P (TREE_TYPE (callop)))
> +  tree stattype
> +    = build_function_type (fn_result, thisarg ? FUNCTION_ARG_CHAIN (callop)
> +				      : TYPE_ARG_TYPES (optype));
> +  stattype = cp_build_type_attribute_variant (stattype,
> +					      TYPE_ATTRIBUTES (optype));
> +  if (flag_noexcept_type && TYPE_NOTHROW_P (TREE_TYPE (callop)))
>       stattype = build_exception_variant (stattype, noexcept_true_spec);

If !thisarg, shouldn't stattype just be optype?  No need to rebuild it 
in that case.

>     if (generic_lambda_p)
> @@ -1249,6 +1266,41 @@ maybe_add_lambda_conv_op (tree type)
>   
>     add_method (type, fn, false);
>   
> +  if (thisarg == NULL_TREE)
> +    {
> +      /* For static lambda, just return operator().  */
> +      if (nested)
> +	push_function_context ();
> +      else
> +	/* Still increment function_depth so that we don't GC in the
> +	   middle of an expression.  */
> +	++function_depth;
> +
> +      /* Generate the body of the conversion op.  */
> +
> +      start_preparsed_function (convfn, NULL_TREE,
> +				SF_PRE_PARSED | SF_INCLASS_INLINE);
> +      tree body = begin_function_body ();
> +      tree compound_stmt = begin_compound_stmt (0);
> +
> +      /* decl_needed_p needs to see that it's used.  */
> +      TREE_USED (callop) = 1;
> +      finish_return_stmt (decay_conversion (callop, tf_warning_or_error));
> +
> +      finish_compound_stmt (compound_stmt);
> +      finish_function_body (body);
> +
> +      fn = finish_function (/*inline_p=*/true);
> +      if (!generic_lambda_p)
> +	expand_or_defer_fn (fn);
> +
> +      if (nested)
> +	pop_function_context ();
> +      else
> +	--function_depth;
> +      return;
> +    }
> +
>     /* Generic thunk code fails for varargs; we'll complain in mark_used if
>        the conversion op is used.  */
>     if (varargs_function_p (callop))
> --- gcc/cp/error.cc.jj	2022-09-13 09:21:01.219921365 +0200
> +++ gcc/cp/error.cc	2022-09-13 12:14:31.675733848 +0200
> @@ -1692,7 +1692,13 @@ dump_lambda_function (cxx_pretty_printer
>   {
>     /* A lambda's signature is essentially its "type".  */
>     dump_type (pp, DECL_CONTEXT (fn), flags);
> -  if (!(TYPE_QUALS (class_of_this_parm (TREE_TYPE (fn))) & TYPE_QUAL_CONST))
> +  if (TREE_CODE (TREE_TYPE (fn)) == FUNCTION_TYPE)
> +    {
> +      pp->padding = pp_before;
> +      pp_c_ws_string (pp, "static");
> +    }
> +  else if (!(TYPE_QUALS (class_of_this_parm (TREE_TYPE (fn)))
> +	     & TYPE_QUAL_CONST))
>       {
>         pp->padding = pp_before;
>         pp_c_ws_string (pp, "mutable");
> --- gcc/c-family/c-cppbuiltin.cc.jj	2022-09-13 09:21:00.986924662 +0200
> +++ gcc/c-family/c-cppbuiltin.cc	2022-09-13 12:14:31.683733738 +0200
> @@ -1081,6 +1081,7 @@ c_cpp_builtins (cpp_reader *pfile)
>   	  cpp_define (pfile, "__cpp_constexpr=202110L");
>   	  cpp_define (pfile, "__cpp_multidimensional_subscript=202110L");
>   	  cpp_define (pfile, "__cpp_named_character_escapes=202207L");
> +	  cpp_define (pfile, "__cpp_static_call_operator=202207L");
>   	}
>         if (flag_concepts)
>           {
> --- gcc/testsuite/g++.dg/template/error30.C.jj	2020-05-07 23:10:25.242966216 +0200
> +++ gcc/testsuite/g++.dg/template/error30.C	2022-09-13 14:43:49.624224804 +0200
> @@ -2,4 +2,4 @@
>   
>   template<int> struct A;
>   
> -template<template<typename> class B> A<B<int>::x> operator() (); // { dg-error "51:.A<B<int>::x> operator\\(\\)\\(\\). must be a non-static member function" }
> +template<template<typename> class B> A<B<int>::x> operator() (); // { dg-error "51:.A<B<int>::x> operator\\(\\)\\(\\). must be a member function" }
> --- gcc/testsuite/g++.dg/cpp1z/constexpr-lambda13.C.jj	2020-01-14 20:02:46.786609789 +0100
> +++ gcc/testsuite/g++.dg/cpp1z/constexpr-lambda13.C	2022-09-13 14:45:42.659698626 +0200
> @@ -1,5 +1,5 @@
>   // { dg-do compile { target c++17 } }
>   
>   auto l1 = []() constexpr constexpr { }; // { dg-error "duplicate" }
> -auto l2 = []() mutable mutable { }; // { dg-error "duplicate" }
> -auto l3 = []() static { };	    // { dg-error "static" }
> +auto l2 = []() mutable mutable { }; // { dg-error "conflicting lambda specifiers" }
> +auto l3 = []() static { };	    // { dg-error "static' only valid in lambda with" "" { target c++20_down } }
> --- gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C.jj	2022-08-26 09:24:12.171614876 +0200
> +++ gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C	2022-09-13 15:05:16.557821712 +0200
> @@ -563,3 +563,9 @@
>   #elif __cpp_named_character_escapes != 202207
>   #  error "__cpp_named_character_escapes != 202207"
>   #endif
> +
> +#ifndef __cpp_static_call_operator
> +#  error "__cpp_static_call_operator"
> +#elif __cpp_static_call_operator != 202207
> +#  error "__cpp_static_call_operator != 202207"
> +#endif
> --- gcc/testsuite/g++.dg/cpp23/static-operator-call1.C.jj	2022-09-13 13:01:34.251875648 +0200
> +++ gcc/testsuite/g++.dg/cpp23/static-operator-call1.C	2022-09-13 13:00:51.344457645 +0200
> @@ -0,0 +1,41 @@
> +// P1169R4 - static operator()
> +// { dg-do compile { target c++11 } }
> +// { dg-options "" }
> +
> +template <typename T>
> +struct S
> +{
> +  static constexpr bool operator () (T const &x, T const &y) { return x < y; }; // { dg-warning "may be a static member function only with" "" { target c++20_down } }
> +  using P = bool (*) (T const &, T const &);
> +  operator P () const { return operator (); }
> +};
> +
> +static_assert (S<int> {} (1, 2), "");
> +
> +template <typename T>
> +void
> +bar (T &x)
> +{
> +  x (1, 2);
> +}
> +
> +void
> +foo ()
> +{
> +#if __cpp_constexpr >= 201603L
> +  auto a = [](int x, int y) static constexpr { return x + y; };			// { dg-warning "'static' only valid in lambda with" "" { target { c++17 && c++20_down } } }
> +  static_assert (a (1, 2) == 3, "");
> +  bar (*a);
> +#endif
> +  auto b = []() static { return 1; };						// { dg-warning "'static' only valid in lambda with" "" { target c++20_down } }
> +  b ();
> +  auto c = [](int x, int y) static { return x + y; };				// { dg-warning "'static' only valid in lambda with" "" { target c++20_down } }
> +  c (1, 2);
> +  bar (*c);
> +#if __cpp_generic_lambdas >= 201707L
> +  auto d = []<typename T, typename U>(T x, U y) static { return x + y; };	// { dg-warning "'static' only valid in lambda with" "" { target c++20_only } }
> +  d (1, 2L);
> +#endif
> +  S<long> s;
> +  s(1L, 2L);
> +}
> --- gcc/testsuite/g++.dg/cpp23/static-operator-call2.C.jj	2022-09-13 13:01:56.307576484 +0200
> +++ gcc/testsuite/g++.dg/cpp23/static-operator-call2.C	2022-09-13 13:30:24.092305299 +0200
> @@ -0,0 +1,22 @@
> +// P1169R4 - static operator()
> +// { dg-do compile { target c++11 } }
> +// { dg-options "" }
> +
> +void
> +foo ()
> +{
> +  int u = 0;
> +  auto a = [](int x, int y) mutable mutable { return x + y; };		// { dg-error "conflicting lambda specifiers" }
> +  auto b = [](int x, int y) static static { return x + y; };		// { dg-error "conflicting lambda specifiers" }
> +									// { dg-warning "'static' only valid in lambda with" "" { target c++20_down } .-1 }
> +  auto c = [](int x, int y) static mutable { return x + y; };		// { dg-error "conflicting lambda specifiers" }
> +									// { dg-warning "'static' only valid in lambda with" "" { target c++20_down } .-1 }
> +  auto d = [](int x, int y) mutable static { return x + y; };		// { dg-error "conflicting lambda specifiers" }
> +									// { dg-warning "'static' only valid in lambda with" "" { target c++20_down } .-1 }
> +  auto e = [=](int x, int y) static { return x + y; };			// { dg-error "lambda specifier with lambda capture" }
> +									// { dg-warning "'static' only valid in lambda with" "" { target c++20_down } .-1 }
> +  auto f = [&](int x, int y) static { return x + y; };			// { dg-error "lambda specifier with lambda capture" }
> +									// { dg-warning "'static' only valid in lambda with" "" { target c++20_down } .-1 }
> +  auto g = [u](int x, int y) static { return x + y; };			// { dg-error "lambda specifier with lambda capture" }
> +									// { dg-warning "'static' only valid in lambda with" "" { target c++20_down } .-1 }
> +}
> --- gcc/testsuite/g++.old-deja/g++.jason/operator.C.jj	2020-05-07 23:10:25.242966216 +0200
> +++ gcc/testsuite/g++.old-deja/g++.jason/operator.C	2022-09-13 14:48:43.219257874 +0200
> @@ -6,7 +6,7 @@ typedef __SIZE_TYPE__ size_t;
>   
>   struct A {
>     int operator?:(int a, int b);	   // { dg-error "prohibits overloading" }
> -  static int operator()(int a);	   // { dg-error "14:.static int A::operator\\(\\)\\(int\\). must be a non-static member function" }
> +  static int operator()(int a);	   // { dg-warning "14:.static int A::operator\\(\\)\\(int\\). may be a static member function only with" "" { target c++20_down } }
>     static int operator+(A,A);	   // { dg-error "14:.static int A::operator\\+\\(A, A\\). must be either a non-static member function or a non-member function" }
>     int operator+(int a, int b = 1); // { dg-error "7:.int A::operator\\+\\(int, int\\). must have either zero or one argument" }
>     int operator++(char);		   // { dg-error "7:postfix .int A::operator\\+\\+\\(char\\). must have .int. as its argument" }
> 
> 	Jakub
> 


  reply	other threads:[~2022-09-16 23:24 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-09-13 16:42 Jakub Jelinek
2022-09-16 23:23 ` Jason Merrill [this message]
2022-09-17  6:42   ` Jakub Jelinek
2022-09-17 11:30     ` Jason Merrill
2022-09-19 12:25       ` [PATCH] c++, v2: " Jakub Jelinek
2022-09-27  2:27         ` Jason Merrill
2022-09-19  7:24   ` [PATCH] c++: Improve diagnostics about conflicting specifiers Jakub Jelinek
2022-09-27  0:28     ` Jason Merrill

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=35c635ad-118f-63bd-eb58-cd949286e28d@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).