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++, v2: Implement C++23 P1169R4 - static operator() [PR106651]
Date: Mon, 26 Sep 2022 22:27:34 -0400	[thread overview]
Message-ID: <4f3f00fb-e4ec-5d7f-ff8f-2445abd314cd@redhat.com> (raw)
In-Reply-To: <Yyhf0bMfpvB1cz/a@tucnak>

On 9/19/22 08:25, Jakub Jelinek wrote:
> Hi!
> 
> On Sat, Sep 17, 2022 at 01:30:08PM +0200, Jason Merrill wrote:
> Below is an updated patch on top of the
> https://gcc.gnu.org/pipermail/gcc-patches/2022-September/601788.html
> patch.
> 
>> Ah, OK.  I don't think you need to check for C++23 or CALL_EXPR at all, just
>> CONVERSION_RANK of the other candidate conversion.
> 
> You mean that for C++20 and earlier without static operator () being
> involved it is impossible to see non-standard conversion on the first
> argument?

I believe so, and even if it were possible the change is correct for 
earlier standards.

> I've instrumented GCC during the weekend to log an cases where
> static_1 != static_2 and log also whether the corresponding arg on non-static
> has standard-conversion or not and the only cases I saw were with
> the standard conversion when this static operator () patch wasn't in,
> all similar to:
> struct S {
>    int foo (long);
>    static int foo (long long);
> };
> 
> void
> baz ()
> {
>    S s;
>    s.foo (1);
> }
> and I can't imagine how there could be something other than standard
> conversion for the object on which it is called in those cases.
> 
>> And I notice that you have winner = -1 in both cases; the first should be
>> winner = 1.
> 
> Thanks for noticing that, I actually meant to write winner = -1; in the
> first and winner = 1; for the second, but that was only because of
> misreading it that -1 is when cand1 is better.
> 
>>>> You could use FUNCTION_FIRST_USER_PARM to skip 'this' if present.
>>>
>>> Ok, will try.
> 
> Done.
> 
>> You can use FUNCTION_FIRST_USER_PARMTYPE instead of the ?:.
> 
> Done too.
> 
>> The reformatting of the next two statements seems unnecessary.
> 
> It is unnecessary (and I left it now out), I was just something
> that catched my eye that it could be formatted more nicely.
> 
>> How about
>>
>> void f()
>> {
>>    auto l = [] (auto x) static { return x; };
>>    int (*p)(int) = l;
>> }
>>
>> ?
> 
> You're right.  Except that we ICE on the above for C++14 and 17 in
> tsubst_copy_and_build's
> 20343		else if (identifier_p (templ))
> 20344		  {
> 20345		    /* C++20 P0846: we can encounter an IDENTIFIER_NODE here when
> 20346		       name lookup found nothing when parsing the template name.  */
> 20347		    gcc_assert (cxx_dialect >= cxx20 || seen_error ());
> 20348		    RETURN (tid);
> 20349		  }
> and while for C++20 and 23 we don't, we say
> error: cannot convert ‘f()::<lambda(auto:1)>’ to ‘int (*)(int)’ in initialization
> so I think it is just that the assertion does nothing for C++20/23 and we
> didn't manage to look up templ
>   <identifier_node 0x7fffea1fdec0 operator() tree_2 simple-op local bindings <0x7fffea3343e8>>
> 
> Just as a wild guess, I've tried the attached incremental patch on top of
> the inline patch below and both the ICEs and errors are gone, but whether
> that is correct or not sadly no idea.

That makes sense.  The patch with followup is OK.

> 2022-09-19  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.  If 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 18:57:29.356969560 +0200
> +++ gcc/cp/cp-tree.h	2022-09-19 11:53:19.780594924 +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-19 11:52:53.140960839 +0200
> +++ gcc/cp/parser.cc	2022-09-19 12:11:15.891823798 +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,
> @@ -11719,6 +11719,18 @@ cp_parser_lambda_declarator_opt (cp_pars
>         LAMBDA_EXPR_MUTABLE_P (lambda_expr) = 1;
>         quals = TYPE_UNQUALIFIED;
>       }
> +  else if (lambda_specs.storage_class == sc_static)
> +    {
> +      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;
> +	}
> +    }
>   
>     tx_qual = cp_parser_tx_qualifier_opt (parser);
>     if (omitted_parms_loc && tx_qual)
> @@ -11804,6 +11816,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);
>   
> @@ -11827,8 +11845,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)
> @@ -16022,8 +16041,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-19 11:52:53.169960441 +0200
> +++ gcc/cp/decl.cc	2022-09-19 11:53:19.818594402 +0200
> @@ -15294,8 +15294,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 18:57:10.199226101 +0200
> +++ gcc/cp/call.cc	2022-09-19 13:28:53.511058032 +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,20 @@ 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.  */
> +	  if (CONVERSION_RANK (cand2->convs[0]) >= cr_user)
> +	    winner = 1;
> +	  off2 = 1;
> +	}
>         else
>   	{
> +	  if (CONVERSION_RANK (cand1->convs[0]) >= cr_user)
> +	    winner = -1;
>   	  off1 = 1;
>   	  --len;
>   	}
> --- gcc/cp/lambda.cc.jj	2022-09-13 18:57:10.267225191 +0200
> +++ gcc/cp/lambda.cc	2022-09-19 12:34:46.638522537 +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,11 @@ maybe_add_lambda_conv_op (tree type)
>     tree fn_args = NULL_TREE;
>     {
>       int ix = 0;
> -    tree src = DECL_CHAIN (DECL_ARGUMENTS (callop));
> +    tree src = FUNCTION_FIRST_USER_PARM (callop);
>       tree tgt = NULL;
>   
> +    if (!thisarg && !decltype_call)
> +      src = NULL_TREE;
>       while (src)
>         {
>   	tree new_node = copy_node (src);
> @@ -1160,12 +1168,15 @@ maybe_add_lambda_conv_op (tree type)
>   	if (generic_lambda_p)
>   	  {
>   	    tree a = tgt;
> -	    if (DECL_PACK_P (tgt))
> +	    if (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 +1204,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,10 +1214,14 @@ 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));
> +  tree stattype
> +    = build_function_type (fn_result, FUNCTION_FIRST_USER_PARMTYPE (callop));
>     stattype = (cp_build_type_attribute_variant
>   	      (stattype, TYPE_ATTRIBUTES (optype)));
>     if (flag_noexcept_type
> @@ -1249,6 +1264,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 18:57:10.249225432 +0200
> +++ gcc/cp/error.cc	2022-09-19 11:53:19.851593949 +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 18:57:10.128227052 +0200
> +++ gcc/c-family/c-cppbuiltin.cc	2022-09-19 11:53:19.873593647 +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-07-28 15:39:10.020756063 +0200
> +++ gcc/testsuite/g++.dg/template/error30.C	2022-09-19 11:53:19.880593550 +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-12 11:54:37.127402637 +0100
> +++ gcc/testsuite/g++.dg/cpp1z/constexpr-lambda13.C	2022-09-19 12:47:20.398209828 +0200
> @@ -2,4 +2,4 @@
>   
>   auto l1 = []() constexpr constexpr { }; // { dg-error "duplicate" }
>   auto l2 = []() mutable mutable { }; // { dg-error "duplicate" }
> -auto l3 = []() static { };	    // { dg-error "static" }
> +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-09-13 18:57:10.408223302 +0200
> +++ gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C	2022-09-19 11:53:19.907593180 +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-19 11:53:19.908593166 +0200
> +++ gcc/testsuite/g++.dg/cpp23/static-operator-call1.C	2022-09-19 13:32:10.095362732 +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-19 11:53:19.908593166 +0200
> +++ gcc/testsuite/g++.dg/cpp23/static-operator-call2.C	2022-09-19 12:54:54.226996543 +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 "duplicate 'mutable' specifier" }
> +  auto b = [](int x, int y) static static { return x + y; };		// { dg-error "duplicate 'static' specifier" }
> +									// { 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 "'mutable' specifier conflicts with 'static'" }
> +									// { 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 "'static' specifier conflicts with 'mutable'" }
> +									// { 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-07-28 15:39:10.024756008 +0200
> +++ gcc/testsuite/g++.old-deja/g++.jason/operator.C	2022-09-19 11:53:19.918593029 +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-27  2:27 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-09-13 16:42 [PATCH] c++: " Jakub Jelinek
2022-09-16 23:23 ` Jason Merrill
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 [this message]
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=4f3f00fb-e4ec-5d7f-ff8f-2445abd314cd@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).