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 13:30:08 +0200	[thread overview]
Message-ID: <98c787d4-6e2c-411b-0248-fd7a765e0acc@redhat.com> (raw)
In-Reply-To: <YyVsTZMB7ZaQDE6X@tucnak>

On 9/17/22 02:42, Jakub Jelinek wrote:
> On Sat, Sep 17, 2022 at 01:23:59AM +0200, Jason Merrill wrote:
>> On 9/13/22 12:42, Jakub Jelinek wrote:
>>> 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.
> 
> I will try that.
> 
>>> 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?
> 
> That is either on the
> struct less {
>      static constexpr auto operator()(int i, int j) -> bool {
>          return i < j;
>      }
> 
>      using P = bool(*)(int, int);
>      operator P() const { return operator(); }
> };
> 
> static_assert(less{}(1, 2));
> testcase from the paper (S in static-operator-call1.C) or any static lambda
> which also has static operator() and cast operator.
> The paper then talks about
> operator()(contrived-parameter, int, int);
> call-function(bool(*)(int, int), int, int);
> which is exactly what caused the ICE, one of the cand?->fn was the static
> operator(), the other cand?->fn isn't a FUNCTION_DECL, but a function
> pointer (what the cast operator returns).

Ah, makes sense.

>>> +	{
>>> +	  /* 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().
> 
> It doesn't work for the static operator() case vs. pointer returned by
> cast operator, with just the
> -      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)
> changes in joust so that it doesn't ICE, the call is ambiguous.
> Previously the standard had:
> "If F is a static member function, ICS1(F) is defined such that ICS1(F) is neither better
> nor worse than ICS1(G) for any function G, and, symmetrically, ICS1(G) is neither better
> nor worse than ICS1(F); otherwise,"
> but that was removed and instead:
> "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."
> was added elsewhere.  The code implements the former.  We don't have any
> conversion for the non-existing object parameter to static, so what I want
> to do is keep doing what we were before if the other candidate's conversion
> on the first parameter is standard (CONVERSION_RANK (cand2->convs[0]) < cr_user)
> and otherwise indicate that the static operator() has there a standard
> conversion and so it should be better than any user/ellipsis/bad conversion.
> Now, it could be done just for cxx_dialect >= cxx23 as that is pedantically
> what the standard says, but if we allow with a pedwarn static operator()
> in earlier standards, I think we better treat it there the same, because
> otherwise one can't call any static lambdas (all those calls would be
> ambiguous).

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.

And I notice that you have winner = -1 in both cases; the first should 
be winner = 1.

>>> @@ -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.
> 
> Ok, will try.
> 
>>> +    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.
> 
> The point was that make_pack_expansion etc. is only used for what
> is later then stored to CALL_EXPR_ARG.  As we don't need to construct
> call at all for the static lambda case, that looked like wasted effort.

Ah, you're right, I thought it was also used for decltype_call.

>>> @@ -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.
> 
> I thought it should be auto-deduced if it isn't yet.

Ah, yes, of course.

You can use FUNCTION_FIRST_USER_PARMTYPE instead of the ?:.

The reformatting of the next two statements seems unnecessary.

> If it doesn't need to
> be, sure, we wouldn't need to bother with decltype_call for !thisarg
> either and just not do the loop on arguments etc.
> I've tried to create a testcase where I could see the generic static lambda
> but I failed to construct one, even with the template <...> lambda syntax
> I can't use a template id of the lambda to instantiate some particular case
> and then use *.  Would passing a generic lambda to a function with
> some function pointer argument trigger it?

How about

void f()
{
   auto l = [] (auto x) static { return x; };
   int (*p)(int) = l;
}

?

Jason


  reply	other threads:[~2022-09-17 11:30 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
2022-09-17  6:42   ` Jakub Jelinek
2022-09-17 11:30     ` Jason Merrill [this message]
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=98c787d4-6e2c-411b-0248-fd7a765e0acc@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).