On 2026-09-25 09:52:55, Jason Merrill wrote:
> On 9/24/26 8:58 PM, Eczbek wrote:
>> On 2026-09-24 19:06:05, Jason Merrill wrote:
>>> On 9/23/26 10:12 AM, Eczbek wrote:
>>>> On 2026-09-22 17:41:12, Jason Merrill wrote:
>>>>> On 9/22/26 8:19 AM, Eczbek wrote:
>>>>>> On 2026-09-21 13:05:37, Jason Merrill wrote:
>>>>>>> On 6/10/26 4:06 PM, Eczbek wrote:
>>>>>>>> On 6/10/26 15:39, Patrick Palka wrote:
>>>>>>>>> On Wed, 10 Jun 2026, Eczbek wrote:
>>>>>>>>>
>>>>>>>>>> On 6/10/26 09:27, Patrick Palka wrote:
>>>>>>>>>>> On Tue, 9 Jun 2026, Eczbek wrote:
>>>>>>>>>>>
>>>>>>>>>>>> On 6/9/26 10:08, Patrick Palka wrote:
>>>>>>>>>>>>> On Mon, 8 Jun 2026, Eczbek wrote:
>>>>>>>>>>>>>
>>>>>>>>>>>>>> On 6/5/26 11:16, Patrick Palka wrote:
>>>>>>>>>>>>>>> On Thu, 4 Jun 2026, Eczbek wrote:
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> On 6/2/26 16:30, Patrick Palka wrote:
>>>>>>>>>>>>>>>>> On Mon, 1 Jun 2026, Eczbek wrote:
>>>>>>>>>>>>>>>>>> On 6/1/26 16:33, Jason Merrill wrote:
>>>>>>>>>>>>>>>>>>> This assumes a template with a single template parameter; 
>>>>>>>>>>>>>>>>>>> it doesn't work for e.g.
>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>> struct A {
>>>>>>>>>>>>>>>>>>>       template<typename T, typename U>
>>>>>>>>>>>>>>>>>>>       operator T U::*() {
>>>>>>>>>>>>>>>>>>>         return 0;
>>>>>>>>>>>>>>>>>>>       }
>>>>>>>>>>>>>>>>>>> };
>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>> int main() {
>>>>>>>>>>>>>>>>>>>       (void) &A::operator int A::*;
>>>>>>>>>>>>>>>>>>> }
>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>> to handle the general case you need to deduce the template 
>>>>>>>>>>>>>>>>>>> arguments.
>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>> Thanks, I did not consider multiple template parameters. 
>>>>>>>>>>>>>>>>>> Would this be similar to what TEMPLATE_ID_EXPR does below, 
>>>>>>>>>>>>>>>>>> or is there some existing helper that should be used? I'm 
>>>>>>>>>>>>>>>>>> not sure how to do this.
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> There are a couple of entrypoints to deduction, all of which 
>>>>>>>>>>>>>>>>> ultimately
>>>>>>>>>>>>>>>>> call the main workhorse unify.  For simplicity I think we 
>>>>>>>>>>>>>>>>> could get away
>>>>>>>>>>>>>>>>> with calling unify directly from resolve_nondeduced_context 
>>>>>>>>>>>>>>>>> with
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>       tparms = DECL_TEMPLATE_PARMS of the conversion function 
>>>>>>>>>>>>>>>>> template
>>>>>>>>>>>>>>>>>       targs = empty TREE_VEC same length as 
>>>>>>>>>>>>>>>>> DECL_TEMPLATE_PARMS
>>>>>>>>>>>>>>>>>       parm = return type of the template
>>>>>>>>>>>>>>>>>       arg = BASELINK_OPTYPE
>>>>>>>>>>>>>>>>>       strict = UNIFY_ALLOW_NONE (I think?)
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> If unify succeeds, then 'targs' will contain the deduced 
>>>>>>>>>>>>>>>>> template
>>>>>>>>>>>>>>>>> arguments that we need to instantiate the conversion function 
>>>>>>>>>>>>>>>>> template
>>>>>>>>>>>>>>>>> with.
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> So for
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>       struct A {
>>>>>>>>>>>>>>>>>         template<class T, class U>
>>>>>>>>>>>>>>>>>         operator B<T*, U&>();
>>>>>>>>>>>>>>>>>       };
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>       int main() {
>>>>>>>>>>>>>>>>>         &A::operator B<int*, char&>;
>>>>>>>>>>>>>>>>>       }
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> we need to call unify with (where {} is shorthand for a 
>>>>>>>>>>>>>>>>> TREE_VEC)
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>       tparms = {T, U}
>>>>>>>>>>>>>>>>>       targs = {NULL, NULL}
>>>>>>>>>>>>>>>>>       parm = B<T*, U&>
>>>>>>>>>>>>>>>>>       arg = B<int*, char&>
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> which should succeed and fill in targs with {int, char}.  
>>>>>>>>>>>>>>>>> Instantiating
>>>>>>>>>>>>>>>>> the template with {int, char} yields the correct 
>>>>>>>>>>>>>>>>> specialization
>>>>>>>>>>>>>>>>> operator B<int*, char&>.
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> Thank you, I made something work! Bootstrapped/regtested again.
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> Jason mentioned that what I wrote in the changelog should be 
>>>>>>>>>>>>>>>> in the main commit message, so what should I write in the 
>>>>>>>>>>>>>>>> changelog instead?
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> I think Jason's point is that the main commit message generally
>>>>>>>>>>>>>>> shouldn't be empty, even if it just repeats what you wrote in 
>>>>>>>>>>>>>>> the
>>>>>>>>>>>>>>> ChangeLog.  Though generally the ChangeLog ought to describe 
>>>>>>>>>>>>>>> how the
>>>>>>>>>>>>>>> code is changed, and the context/motivation of the change 
>>>>>>>>>>>>>>> should be in
>>>>>>>>>>>>>>> the main commit message, so there shouldn't be too much overlap.
>>>>>>>>>>>>>>> Here I'd just write:
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>         (resolve_overloaded_unification): Call unify when 
>>>>>>>>>>>>>>> resolving a
>>>>>>>>>>>>>>>         conversion-function-id.
>>>>>>>>>>>>>>>         (resolve_nondeduced_context): Likewise.
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> -- >8 --
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> ---
>>>>>>>>>>>>>>>>      gcc/cp/pt.cc                           | 30 
>>>>>>>>>>>>>>>> ++++++++++++++++++++++++--
>>>>>>>>>>>>>>>>      gcc/testsuite/g++.dg/template/conv22.C | 28 
>>>>>>>>>>>>>>>> ++++++++++++++++++++++++
>>>>>>>>>>>>>>>>      2 files changed, 56 insertions(+), 2 deletions(-)
>>>>>>>>>>>>>>>>      create mode 100644 gcc/testsuite/g++.dg/template/conv22.C
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
>>>>>>>>>>>>>>>> index 6992b5196fe..d914a1c817d 100644
>>>>>>>>>>>>>>>> --- a/gcc/cp/pt.cc
>>>>>>>>>>>>>>>> +++ b/gcc/cp/pt.cc
>>>>>>>>>>>>>>>> @@ -25193,6 +25193,7 @@ resolve_overloaded_unification (tree 
>>>>>>>>>>>>>>>> tparms,
>>>>>>>>>>>>>>>>        int good = 0;
>>>>>>>>>>>>>>>>        tree goodfn = NULL_TREE;
>>>>>>>>>>>>>>>>        bool addr_p;
>>>>>>>>>>>>>>>> +  tree baselink = NULL_TREE;
>>>>>>>>>>>>>>>>          if (TREE_CODE (arg) == ADDR_EXPR)
>>>>>>>>>>>>>>>>          {
>>>>>>>>>>>>>>>> @@ -25212,7 +25213,22 @@ resolve_overloaded_unification (tree 
>>>>>>>>>>>>>>>> tparms,
>>>>>>>>>>>>>>>>          /* Strip baselink information.  */
>>>>>>>>>>>>>>>>        if (BASELINK_P (arg))
>>>>>>>>>>>>>>>> -    arg = BASELINK_FUNCTIONS (arg);
>>>>>>>>>>>>>>>> +    {
>>>>>>>>>>>>>>>> +      baselink = arg;
>>>>>>>>>>>>>>>> +      arg = BASELINK_FUNCTIONS (arg);
>>>>>>>>>>>>>>>> +    }
>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>> +  if (TREE_CODE (arg) == OVERLOAD
>>>>>>>>>>>>>>>> +      && IDENTIFIER_CONV_OP_P (OVL_NAME (arg)))
>>>>>>>>>>>>>>>> +    {
>>>>>>>>>>>>>>>> +      tree tmpl = OVL_FIRST (arg);
>>>>>>>>>>>>>>>> +      tree tparms = DECL_INNERMOST_TEMPLATE_PARMS (tmpl);
>>>>>>>>>>>>>>>> +      tree targs = make_tree_vec (DECL_NTPARMS (tmpl));
>>>>>>>>>>>>>>>> +      tree parm = DECL_CONV_FN_TYPE (DECL_TEMPLATE_RESULT 
>>>>>>>>>>>>>>>> (tmpl));
>>>>>>>>>>>>>>>> +      tree optype = BASELINK_OPTYPE (baselink);
>>>>>>>>>>>>>>>> +      if (!unify (tparms, targs, parm, optype, 
>>>>>>>>>>>>>>>> UNIFY_ALLOW_NONE, false))
>>>>>>>>>>>>>>>> +    arg = lookup_template_function (tmpl, targs);
>>>>>>>>>>>>>>>> +    }
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> This needs to be done in a loop over each conversion template 
>>>>>>>>>>>>>>> in the
>>>>>>>>>>>>>>> overload set because the result of deduction can differ for each
>>>>>>>>>>>>>>> template:
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>       struct A {
>>>>>>>>>>>>>>>         template<class T> operator T();    // #1
>>>>>>>>>>>>>>>         template<class T> operator B<T>(); // #2
>>>>>>>>>>>>>>>       };
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>       int main() {
>>>>>>>>>>>>>>>         &A::operator B<int>; // targs would be {B<int>} for #1 
>>>>>>>>>>>>>>> and {int} for #2
>>>>>>>>>>>>>>>       }
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> And instead of calling lookup_template_function to form a 
>>>>>>>>>>>>>>> template-id, I
>>>>>>>>>>>>>>> think we need to call instantiate_template to directly 
>>>>>>>>>>>>>>> instantiate
>>>>>>>>>>>>>>> each template for which deduction succeeded.
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> We could factor out all this into a helper
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>       tree resolve_conversion_function_id (tree fns, tree 
>>>>>>>>>>>>>>> optype)
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> that takes a conversion operator overload set FNS and target
>>>>>>>>>>>>>>> type OPTYPE and returns an overload set containing the 
>>>>>>>>>>>>>>> instantiated
>>>>>>>>>>>>>>> functions that match the target type.
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> Thanks, I have this:
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> tree
>>>>>>>>>>>>>> resolve_conversion_function_id (tree fns, tree optype)
>>>>>>>>>>>>>> {
>>>>>>>>>>>>>>       tree overloads = NULL_TREE;
>>>>>>>>>>>>>>       for (lkp_iterator iter (fns); iter; ++iter)
>>>>>>>>>>>>>>         {
>>>>>>>>>>>>>>           tree tmpl = *iter;
>>>>>>>>>>>>>>           tree tparms = DECL_INNERMOST_TEMPLATE_PARMS (tmpl);
>>>>>>>>>>>>>>           tree targs = make_tree_vec (DECL_NTPARMS (tmpl));
>>>>>>>>>>>>>>           tree parm = DECL_CONV_FN_TYPE (DECL_TEMPLATE_RESULT 
>>>>>>>>>>>>>> (tmpl));
>>>>>>>>>>>>>>           if (unify (tparms, targs, parm, optype, 
>>>>>>>>>>>>>> UNIFY_ALLOW_NONE, false))
>>>>>>>>>>>>>>        continue;
>>>>>>>>>>>>>>           tree fn = instantiate_template (tmpl, targs, tf_none);
>>>>>>>>>>>>>>           if (!constraints_satisfied_p (fn))
>>>>>>>>>>>>>>        continue;
>>>>>>>>>>>>>>           overloads = lookup_add (fn, overloads);
>>>>>>>>>>>>>>         }
>>>>>>>>>>>>>>       return overloads;
>>>>>>>>>>>>>> }
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> But how can the most "specific" overload be selected from the 
>>>>>>>>>>>>>> returned set? Please advise.
>>>>>>>>>>>>>
>>>>>>>>>>>>> That's the partial ordering step of overload resolution, done by 
>>>>>>>>>>>>> the the
>>>>>>>>>>>>> 'tourney' function, see e.g perform_overload_resolution.  But I 
>>>>>>>>>>>>> don't think we
>>>>>>>>>>>>> want to do do partial ordering here, we should just reject the 
>>>>>>>>>>>>> code if there's
>>>>>>>>>>>>> more than one viable overload after this step.  So the previous 
>>>>>>>>>>>>> example
>>>>>>>>>>>>>
>>>>>>>>>>>>>        struct A {
>>>>>>>>>>>>>          template<class T> operator T();    // #1
>>>>>>>>>>>>>          template<class T> operator B<T>(); // #2
>>>>>>>>>>>>>        };
>>>>>>>>>>>>>          int main() {
>>>>>>>>>>>>>          &A::operator B<int>; // targs would be {B<int>} for #1 
>>>>>>>>>>>>> and {int} for #2
>>>>>>>>>>>>>        }
>>>>>>>>>>>>>
>>>>>>>>>>>>> should be rejected despite #2 being more specialized than #1.
>>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>> That seems incomplete to me. Both Clang and MSVC appear to select 
>>>>>>>>>>>> the most specialized overload: https://godbolt.org/z/q5a755bcK
>>>>>>>>>>>
>>>>>>>>>>> Good catch. GCC behaves the same and selects the most specialized 
>>>>>>>>>>> conversion
>>>>>>>>>>> function if we turn f into a non-template to sidestep template 
>>>>>>>>>>> argument
>>>>>>>>>>> deduction:
>>>>>>>>>>>
>>>>>>>>>>>       void f(B<int>(A::*)());
>>>>>>>>>>>       int main() { f(&A::operator B<int>); }
>>>>>>>>>>>
>>>>>>>>>>> So the problem is that deduction fails to deduce T=B<int> for the 
>>>>>>>>>>> function
>>>>>>>>>>> template f.  Once we can convince GCC of that then it should work 
>>>>>>>>>>> by virtue of
>>>>>>>>>>> the non-template f case working.
>>>>>>>>>>>
>>>>>>>>>>> The analogous non-conversion-function testcase is:
>>>>>>>>>>>
>>>>>>>>>>>         template<class>
>>>>>>>>>>>         struct B {};
>>>>>>>>>>>
>>>>>>>>>>>         struct A {
>>>>>>>>>>>           template<class T> void g(T);
>>>>>>>>>>>           template<class T> void g(T) requires true;
>>>>>>>>>>>         };
>>>>>>>>>>>
>>>>>>>>>>>         template<class T>
>>>>>>>>>>>         void f(void(A::*)(T));
>>>>>>>>>>>
>>>>>>>>>>>         int main() { f(&A::g<int>); }
>>>>>>>>>>>
>>>>>>>>>>> which GCC does accept by deducing T=int for f, despite multiple 
>>>>>>>>>>> function
>>>>>>>>>>> templates in the overload set.  So the problem is specific to 
>>>>>>>>>>> conversion
>>>>>>>>>>> functions.
>>>>>>>>>>>
>>>>>>>>>>> Using your resolve_conversion_function_id implementation, the 
>>>>>>>>>>> following
>>>>>>>>>>> change to resolve_overloaded_unification seems to do the right thing
>>>>>>>>>>> without needing to implement additional "more specialized" logic:
>>>>>>>>>>>
>>>>>>>>>>> diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
>>>>>>>>>>> index 891e89f1d763..3f079457b455 100644
>>>>>>>>>>> --- a/gcc/cp/pt.cc
>>>>>>>>>>> +++ b/gcc/cp/pt.cc
>>>>>>>>>>> @@ -25336,8 +25336,12 @@ resolve_overloaded_unification (tree 
>>>>>>>>>>> tparms,
>>>>>>>>>>>          arg = TREE_OPERAND (arg, 1);
>>>>>>>>>>>
>>>>>>>>>>>        /* Strip baselink information.  */
>>>>>>>>>>> +  tree optype = NULL_TREE;
>>>>>>>>>>>        if (BASELINK_P (arg))
>>>>>>>>>>> +    {
>>>>>>>>>>> +      optype = BASELINK_OPTYPE (arg);
>>>>>>>>>>>            arg = BASELINK_FUNCTIONS (arg);
>>>>>>>>>>> +    }
>>>>>>>>>>>
>>>>>>>>>>>        if (TREE_CODE (arg) == TEMPLATE_ID_EXPR)
>>>>>>>>>>>          {
>>>>>>>>>>> @@ -25401,6 +25405,9 @@ resolve_overloaded_unification (tree tparms,
>>>>>>>>>>>             not just the function on its own.  */
>>>>>>>>>>>          return false;
>>>>>>>>>>>        else
>>>>>>>>>>> +    {
>>>>>>>>>>> +      if (optype)
>>>>>>>>>>> +       arg = resolve_conversion_function_id (arg, optype);
>>>>>>>>>>>            for (lkp_iterator iter (arg); iter; ++iter)
>>>>>>>>>>>             {
>>>>>>>>>>>               tree fn = *iter;
>>>>>>>>>>> @@ -25417,6 +25424,7 @@ resolve_overloaded_unification (tree tparms,
>>>>>>>>>>>                   ++good;
>>>>>>>>>>>                 }
>>>>>>>>>>>             }
>>>>>>>>>>> +    }
>>>>>>>>>>>
>>>>>>>>>>>        /* [temp.deduct.type] A template-argument can be deduced 
>>>>>>>>>>> from a pointer
>>>>>>>>>>>           to function or pointer to member function argument if the 
>>>>>>>>>>> set of
>>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> Thanks, that seems to work! Here's what I have so far, and my tests. 
>>>>>>>>>> (void)&D::operator int D::*; currently fails because 
>>>>>>>>>> resolve_nondeduced_context only accepts single overloads. How should 
>>>>>>>>>> it also select the most specialized overload?
>>>>>>>>>
>>>>>>>>> I'm not sure it should.  Clang doesn't accept that code either, right?
>>>>>>>>> In any case, let's ignore that case for now and treat it as a separate
>>>>>>>>> issue because we also don't accept the non-convfn version of that 
>>>>>>>>> code:
>>>>>>>>>
>>>>>>>>>         template<class>
>>>>>>>>>         struct B {};
>>>>>>>>>
>>>>>>>>>         struct A {
>>>>>>>>>           template<class T> void g(T);
>>>>>>>>>           template<class T> void g(T) requires true;
>>>>>>>>>         };
>>>>>>>>>
>>>>>>>>>         template<class T>
>>>>>>>>>         void f(void(A::*)(T));
>>>>>>>>>
>>>>>>>>>         int main() {
>>>>>>>>>           &A::g<int>;
>>>>>>>>>         };
>>>>>>>>
>>>>>>>> Clang doesn't accept, but MSVC does.
>>>>>>>
>>>>>>> And EDG.  And I think it's clear we should under
>>>>>>> https://eel.is/c++draft/over.over which does consider the case of no 
>>>>>>> target type.
>>>>>>>
>>>>>>>> I agree it is a good idea to ignore this for now. Is there an existing 
>>>>>>>> bug report?
>>>>>>>
>>>>>>> I think let's not give up on this so quickly, at least not without 
>>>>>>> analyzing it.
>>>>>>>
>>>>>>> It seems that the key difference between this and
>>>>>>>
>>>>>>> auto x = &A::g<int>;
>>>>>>>
>>>>>>> is that in the declaration, we first go through 
>>>>>>> resolve_nondeduced_context (twice, both times to no effect), then 
>>>>>>> resolve_overloaded_unification to determine the type of x, then 
>>>>>>> resolve_address_of_overloaded_function to resolve which actual function 
>>>>>>> we want.
>>>>>>>
>>>>>>> Note that resolve_overloaded_unification is only interested in the 
>>>>>>> type; once we have a type we don't care about the individual overloads. 
>>>>>>>  So if we have optype I think we should just unify on that and not 
>>>>>>> bother with resolve_conversion_function_id.
>>>>>>
>>>>>> To clarify, does this mean we should directly call unify? If so, I'm not 
>>>>>> sure how to get all of the arguments for it here without looping over 
>>>>>> the overloads.
>>>>>
>>>>> I was thinking
>>>>>
>>>>> unify (tparms, targs, parm, optype, strict, explain_p)
>>>>
>>>> Sorry, I'm not sure how to do this properly. This is what I tried:
>>>>
>>>>     if (BASELINK_P (arg)
>>>>         && unify (tparms, targs, parm, BASELINK_OPTYPE (arg), strict,
>>>>          explain_p))
>>>>       return false;
>>>>
>>>> but this breaks all of the `deduce` tests.
>>>
>>> Ah, right, we need a function type, not just the optype.  And now it occurs 
>>> to me that the functions might differ in noexcept even if they match 
>>> otherwise.  So never mind, let's leave resolve_overloaded_unification as in 
>>> your last patch.
>>>
>>>>>>> And then in resolve_nondeduced_context, if we have an optype we can 
>>>>>>> pass that to resolve_address_of_overloaded_function, and again we don't 
>>>>>>> need the new function.
>>>>>>
>>>>>> I'm guessing that would look something like this, but I can't figure out 
>>>>>> how to pass explicit_targs:
>>>>>>
>>>>>>      if (BASELINK_P (expr))
>>>>>>        expr =
>>>>>>          resolve_address_of_overloaded_function (BASELINK_OPTYPE (expr),
>>>>>>                             BASELINK_FUNCTIONS (expr),
>>>>>>                             complain, true,
>>>>>>                             /* explicit_targs? */,
>>>>>>                             BASELINK_ACCESS_BINFO (expr));
>>>>>
>>>>> explicit_targs would be NULL_TREE in this case...
>>>>>> I'm also having trouble with this because 
>>>>>> resolve_address_of_overloaded_function is declared in class.cc but I'm 
>>>>>> in pt.cc. Please advise.
>>>>>
>>>>> ...but it seems it's better to call instantiate_type rather than resolve_ 
>>>>> directly.
>>>>
>>>> This is what I tried:
>>>>
>>>>     if (BASELINK_P (expr))
>>>>       {
>>>>         baselink = expr;
>>>>         expr = instantiate_type (BASELINK_OPTYPE (expr),
>>>>                     BASELINK_FUNCTIONS (expr), complain);
>>>>       }
>>>>
>>>> but this breaks other tests. `&A::operator int;` results in:
>>>>      error: cannot resolve overloaded function ‘operator T’ based on 
>>>> conversion to type ‘int’
>>>
>>> And here we would want to check whether if there are multiple results of 
>>> resolve_conversion_function_id they have the same type, and if so pass that 
>>> type to instantiate_type.
>>
>> To clarify, do you mean that if resolve_conversion_function_id returns an 
>> overload set with more than one overload, we should iterate over every 
>> overload to check if any two have the same function type? I don't understand 
>> the reasoning behind that and how to use instantiate_type here.
> 
> To see if they *all* have the same function type.  And then pass that 
> function type to instantiate_type.

Oh, thanks. I tried this:

      tree fns = resolve_conversion_function_id (expr, optype);
      if (OVL_SINGLE_P (fns))
        {
          goodfn = OVL_FIRST (fns);
          good = 1;
        }
      else if (fns)
        {
          tree type = TREE_TYPE (OVL_FIRST (fns));
          good = 1;
          for (ovl_iterator iter (fns); ++iter;)
            if (!same_type_p (type, TREE_TYPE (*iter)))
              {
                good = 0;
                break;
              }
          if (good)
            goodfn = instantiate_type (type, fns, complain);
          good = goodfn != error_mark_node;
        }

But it seems to only break this case:

error: cannot resolve overloaded function ‘operator int D::*’ based on 
conversion to type ‘int D::* (D::)()’
   77 |   auto d = &D::operator int D::*;
      |                                ^

I also looked at convert_to_void, which says that [over.over] forbids taking 
the address of an overloaded function in `(void)&D::operator int D::*;` or 
`&D::operator int D::*, 0;`. This might mean that my test is correct to fail, 
but do you know which specific paragraph states this?

> 
>>>
>>>> Another case to consider is selecting the overload based on the qualifiers 
>>>> of the target type: https://godbolt.org/z/aY7Gorrx6
>>>> I discussed this with Waffl3x and we believe that Clang is correct to 
>>>> select the template overload. My patch doesn't affect this.
>>>
>>> They are indeed correct.  Normally 
>>> instantiate_type/resolve_address_of_overloaded_function take care of that, 
>>> but presumably they don't consider BASELINK_OPTYPE currently, so perhaps we 
>>> need resolve_conversion_function_id there too.
> 
> ...though the auto declaration worked, so it seems there's something subtler 
> going wrong.

I assume you mean `auto b = &B::operator int;`. Is this not the case because it 
simply picks the non-template overload?

> 
> Jason
> 
>>> Jason
>>>
>>>>>>> And then to handle the non-conversion example above, we could change 
>>>>>>> the existing loop to handle the case where multiple results have the 
>>>>>>> same type, and use resolve_address_of_overloaded_function in that case 
>>>>>>> as well.  Or perhaps replace the existing loop with a call to 
>>>>>>> do_auto_deduction.  This could indeed be a follow-up patch, but I'd 
>>>>>>> like to address it while we're thinking about it rather than drop it 
>>>>>>> until some unknown time in the future.
>>>>>>>
>>>>>>> After these changes, the first resolve_nondeduced_context should fully 
>>>>>>> resolve the expression, so later ones don't need to do anything.  
>>>>>>> Though I also wonder about removing the two calls to 
>>>>>>> resolve_nondeduced_context in the auto path, since they seem to be 
>>>>>>> redundant.
>>>>>>>
>>>>>>> Jason
>>>>>>>
>>>>>>>> Bootstrapped/regtested again. Is the ChangeLog OK?
>>>>>>>>
>>>>>>>> -- >8 --
>>>>>>>>
>>>>>>>> Taking the address of a conversion function template instantiation
>>>>>>>> incorrectly errors.
>>>>>>>>
>>>>>>>>        PR c++/122383
>>>>>>>>
>>>>>>>> gcc/cp/ChangeLog:
>>>>>>>>
>>>>>>>>        * pt.cc (resolve_conversion_function_id): Filter a conversion
>>>>>>>>        function overload set using unify.
>>>>>>>>        (resolve_overloaded_unification): Call
>>>>>>>>        resolve_conversion_function_id when resolving
>>>>>>>>        a conversion-function-id.
>>>>>>>>        (resolve_nondeduced_context): Likewise.
>>>>>>>>
>>>>>>>> gcc/testsuite/ChangeLog:
>>>>>>>>
>>>>>>>>        * g++.dg/template/conv22.C: New test.
>>>>>>>> ---
>>>>>>>>      gcc/cp/pt.cc                           | 121 
>>>>>>>> +++++++++++++++++--------
>>>>>>>>      gcc/testsuite/g++.dg/template/conv22.C |  69 ++++++++++++++
>>>>>>>>      2 files changed, 151 insertions(+), 39 deletions(-)
>>>>>>>>      create mode 100644 gcc/testsuite/g++.dg/template/conv22.C
>>>>>>>>
>>>>>>>> diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
>>>>>>>> index 6992b5196fe..912bb6b5185 100644
>>>>>>>> --- a/gcc/cp/pt.cc
>>>>>>>> +++ b/gcc/cp/pt.cc
>>>>>>>> @@ -25174,6 +25174,31 @@ type_unification_real (tree tparms,
>>>>>>>>        return unify_success (explain_p);
>>>>>>>>      }
>>>>>>>>      +/* Subroutine of resolve_overloaded_unification and
>>>>>>>> +   resolve_nondeduced_context.  FNS is a conversion operator overload 
>>>>>>>> set and
>>>>>>>> +   OPTYPE is the target type.  Returns an overload set containing the
>>>>>>>> +   instantiated functions that match the target type.  */
>>>>>>>> +
>>>>>>>> +tree
>>>>>>>> +resolve_conversion_function_id (tree fns, tree optype)
>>>>>>>> +{
>>>>>>>> +  tree overloads = NULL_TREE;
>>>>>>>> +  for (lkp_iterator iter (fns); iter; ++iter)
>>>>>>>> +    {
>>>>>>>> +      tree tmpl = *iter;
>>>>>>>> +      tree tparms = DECL_INNERMOST_TEMPLATE_PARMS (tmpl);
>>>>>>>> +      tree targs = make_tree_vec (DECL_NTPARMS (tmpl));
>>>>>>>> +      tree parm = DECL_CONV_FN_TYPE (DECL_TEMPLATE_RESULT (tmpl));
>>>>>>>> +      if (unify (tparms, targs, parm, optype, UNIFY_ALLOW_NONE, 
>>>>>>>> false))
>>>>>>>> +    continue;
>>>>>>>> +      tree fn = instantiate_template (tmpl, targs, tf_none);
>>>>>>>> +      if (!constraints_satisfied_p (fn))
>>>>>>>> +    continue;
>>>>>>>> +      overloads = lookup_add (fn, overloads);
>>>>>>>> +    }
>>>>>>>> +  return overloads;
>>>>>>>> +}
>>>>>>>> +
>>>>>>>>      /* Subroutine of type_unification_real.  Args are like the 
>>>>>>>> variables
>>>>>>>>         at the call site.  ARG is an overloaded function (or 
>>>>>>>> template-id);
>>>>>>>>         we try deducing template args from each of the overloads, and 
>>>>>>>> if
>>>>>>>> @@ -25193,6 +25218,7 @@ resolve_overloaded_unification (tree tparms,
>>>>>>>>        int good = 0;
>>>>>>>>        tree goodfn = NULL_TREE;
>>>>>>>>        bool addr_p;
>>>>>>>> +  tree optype = NULL_TREE;
>>>>>>>>          if (TREE_CODE (arg) == ADDR_EXPR)
>>>>>>>>          {
>>>>>>>> @@ -25212,7 +25238,10 @@ resolve_overloaded_unification (tree tparms,
>>>>>>>>          /* Strip baselink information.  */
>>>>>>>>        if (BASELINK_P (arg))
>>>>>>>> -    arg = BASELINK_FUNCTIONS (arg);
>>>>>>>> +    {
>>>>>>>> +      optype = BASELINK_OPTYPE (arg);
>>>>>>>> +      arg = BASELINK_FUNCTIONS (arg);
>>>>>>>> +    }
>>>>>>>>          if (TREE_CODE (arg) == TEMPLATE_ID_EXPR)
>>>>>>>>          {
>>>>>>>> @@ -25276,22 +25305,26 @@ resolve_overloaded_unification (tree tparms,
>>>>>>>>             not just the function on its own.  */
>>>>>>>>          return false;
>>>>>>>>        else
>>>>>>>> -    for (lkp_iterator iter (arg); iter; ++iter)
>>>>>>>> -      {
>>>>>>>> -    tree fn = *iter;
>>>>>>>> -    if (flag_noexcept_type)
>>>>>>>> -      maybe_instantiate_noexcept (fn, tf_none);
>>>>>>>> -    if (TREE_CODE (fn) == FUNCTION_DECL && !constraints_satisfied_p 
>>>>>>>> (fn))
>>>>>>>> -      continue;
>>>>>>>> -    tree elem = TREE_TYPE (fn);
>>>>>>>> -    if (try_one_overload (tparms, targs, tempargs, parm, elem,
>>>>>>>> -                  strict, sub_strict, addr_p, explain_p)
>>>>>>>> -        && (!goodfn || !same_type_p (goodfn, elem)))
>>>>>>>> -      {
>>>>>>>> -        goodfn = elem;
>>>>>>>> -        ++good;
>>>>>>>> -      }
>>>>>>>> -      }
>>>>>>>> +    {
>>>>>>>> +      if (optype)
>>>>>>>> +    arg = resolve_conversion_function_id (arg, optype);
>>>>>>>> +      for (lkp_iterator iter (arg); iter; ++iter)
>>>>>>>> +    {
>>>>>>>> +      tree fn = *iter;
>>>>>>>> +      if (flag_noexcept_type)
>>>>>>>> +        maybe_instantiate_noexcept (fn, tf_none);
>>>>>>>> +      if (TREE_CODE (fn) == FUNCTION_DECL && !constraints_satisfied_p 
>>>>>>>> (fn))
>>>>>>>> +        continue;
>>>>>>>> +      tree elem = TREE_TYPE (fn);
>>>>>>>> +      if (try_one_overload (tparms, targs, tempargs, parm, elem,
>>>>>>>> +                    strict, sub_strict, addr_p, explain_p)
>>>>>>>> +          && (!goodfn || !same_type_p (goodfn, elem)))
>>>>>>>> +        {
>>>>>>>> +          goodfn = elem;
>>>>>>>> +          ++good;
>>>>>>>> +        }
>>>>>>>> +    }
>>>>>>>> +    }
>>>>>>>>          /* [temp.deduct.type] A template-argument can be deduced from 
>>>>>>>> a pointer
>>>>>>>>           to function or pointer to member function argument if the 
>>>>>>>> set of
>>>>>>>> @@ -25335,7 +25368,7 @@ resolve_overloaded_unification (tree tparms,
>>>>>>>>      tree
>>>>>>>>      resolve_nondeduced_context (tree orig_expr, tsubst_flags_t 
>>>>>>>> complain)
>>>>>>>>      {
>>>>>>>> -  tree expr, offset, baselink;
>>>>>>>> +  tree expr, offset, baselink, optype;
>>>>>>>>        bool addr;
>>>>>>>>          if (!type_unknown_p (orig_expr))
>>>>>>>> @@ -25345,6 +25378,7 @@ resolve_nondeduced_context (tree orig_expr, 
>>>>>>>> tsubst_flags_t complain)
>>>>>>>>        addr = false;
>>>>>>>>        offset = NULL_TREE;
>>>>>>>>        baselink = NULL_TREE;
>>>>>>>> +  optype = NULL_TREE;
>>>>>>>>          if (TREE_CODE (expr) == ADDR_EXPR)
>>>>>>>>          {
>>>>>>>> @@ -25359,14 +25393,14 @@ resolve_nondeduced_context (tree orig_expr, 
>>>>>>>> tsubst_flags_t complain)
>>>>>>>>        if (BASELINK_P (expr))
>>>>>>>>          {
>>>>>>>>            baselink = expr;
>>>>>>>> +      optype = BASELINK_OPTYPE (expr);
>>>>>>>>            expr = BASELINK_FUNCTIONS (expr);
>>>>>>>>          }
>>>>>>>>      +  int good = 0;
>>>>>>>> +  tree goodfn = NULL_TREE;
>>>>>>>>        if (TREE_CODE (expr) == TEMPLATE_ID_EXPR)
>>>>>>>>          {
>>>>>>>> -      int good = 0;
>>>>>>>> -      tree goodfn = NULL_TREE;
>>>>>>>> -
>>>>>>>>            /* If we got some explicit template args, we need to plug 
>>>>>>>> them into
>>>>>>>>           the affected templates before we try to unify, in case the
>>>>>>>>           explicit args will completely resolve the templates in 
>>>>>>>> question.  */
>>>>>>>> @@ -25403,28 +25437,37 @@ resolve_nondeduced_context (tree orig_expr, 
>>>>>>>> tsubst_flags_t complain)
>>>>>>>>              }
>>>>>>>>              }
>>>>>>>>          }
>>>>>>>> -      if (good == 1)
>>>>>>>> -    {
>>>>>>>> -      expr = goodfn;
>>>>>>>> -      if (baselink)
>>>>>>>> -        expr = build_baselink (BASELINK_BINFO (baselink),
>>>>>>>> -                   BASELINK_ACCESS_BINFO (baselink),
>>>>>>>> -                   expr, BASELINK_OPTYPE (baselink));
>>>>>>>> -      if (offset)
>>>>>>>> -        {
>>>>>>>> -          tree base
>>>>>>>> -        = TYPE_MAIN_VARIANT (TREE_TYPE (TREE_OPERAND (offset, 0)));
>>>>>>>> -          expr = build_offset_ref (base, expr, addr, complain);
>>>>>>>> -        }
>>>>>>>> -      if (addr)
>>>>>>>> -        expr = cp_build_addr_expr (expr, complain);
>>>>>>>> -      return expr;
>>>>>>>> -    }
>>>>>>>> -      else if (good == 0 && badargs && (complain & tf_error))
>>>>>>>> +      if (good == 0 && badargs && (complain & tf_error))
>>>>>>>>          /* There were no good options and at least one bad one, so 
>>>>>>>> let the
>>>>>>>>             user know what the problem is.  */
>>>>>>>>          instantiate_template (badfn, badargs, complain);
>>>>>>>>          }
>>>>>>>> +  else if (optype)
>>>>>>>> +    {
>>>>>>>> +      expr = resolve_conversion_function_id (expr, optype);
>>>>>>>> +      if (OVL_SINGLE_P (expr))
>>>>>>>> +    {
>>>>>>>> +      goodfn = OVL_FIRST (expr);
>>>>>>>> +      good = 1;
>>>>>>>> +    }
>>>>>>>> +    }
>>>>>>>> +  if (good == 1)
>>>>>>>> +    {
>>>>>>>> +      expr = goodfn;
>>>>>>>> +      if (baselink)
>>>>>>>> +    expr = build_baselink (BASELINK_BINFO (baselink),
>>>>>>>> +                   BASELINK_ACCESS_BINFO (baselink),
>>>>>>>> +                   expr, BASELINK_OPTYPE (baselink));
>>>>>>>> +      if (offset)
>>>>>>>> +    {
>>>>>>>> +      tree base
>>>>>>>> +        = TYPE_MAIN_VARIANT (TREE_TYPE (TREE_OPERAND (offset, 0)));
>>>>>>>> +      expr = build_offset_ref (base, expr, addr, complain);
>>>>>>>> +    }
>>>>>>>> +      if (addr)
>>>>>>>> +    expr = cp_build_addr_expr (expr, complain);
>>>>>>>> +      return expr;
>>>>>>>> +    }
>>>>>>>>        return orig_expr;
>>>>>>>>      }
>>>>>>>>      diff --git a/gcc/testsuite/g++.dg/template/conv22.C 
>>>>>>>> b/gcc/testsuite/g++.dg/template/conv22.C
>>>>>>>> new file mode 100644
>>>>>>>> index 00000000000..7cc85abc8c3
>>>>>>>> --- /dev/null
>>>>>>>> +++ b/gcc/testsuite/g++.dg/template/conv22.C
>>>>>>>> @@ -0,0 +1,69 @@
>>>>>>>> +// PR c++/122383
>>>>>>>> +// { dg-do compile { target c++20 } }
>>>>>>>> +
>>>>>>>> +struct A {
>>>>>>>> +  template<typename T>
>>>>>>>> +  operator T() {
>>>>>>>> +    return T();
>>>>>>>> +  }
>>>>>>>> +};
>>>>>>>> +
>>>>>>>> +struct B {
>>>>>>>> +  operator int() {
>>>>>>>> +    return 0;
>>>>>>>> +  }
>>>>>>>> +
>>>>>>>> +  template<typename T>
>>>>>>>> +  operator T() {
>>>>>>>> +    static_assert(false);
>>>>>>>> +  }
>>>>>>>> +};
>>>>>>>> +
>>>>>>>> +struct C {
>>>>>>>> +  template<typename T>
>>>>>>>> +  operator T() {
>>>>>>>> +    return T();
>>>>>>>> +  }
>>>>>>>> +
>>>>>>>> +  template<typename U>
>>>>>>>> +  operator U() requires(false) {
>>>>>>>> +    return U();
>>>>>>>> +  }
>>>>>>>> +};
>>>>>>>> +
>>>>>>>> +struct D {
>>>>>>>> +  template<typename T>
>>>>>>>> +  operator T() {
>>>>>>>> +    static_assert(false);
>>>>>>>> +  }
>>>>>>>> +
>>>>>>>> +  template<typename T, typename U>
>>>>>>>> +  operator T U::*() {
>>>>>>>> +    return 0;
>>>>>>>> +  }
>>>>>>>> +};
>>>>>>>> +
>>>>>>>> +template<typename R, typename C>
>>>>>>>> +void deduce(R(C::*)()) {}
>>>>>>>> +
>>>>>>>> +template<typename R, typename C1, typename C2>
>>>>>>>> +void deduce2(R C1::*(C2::*)()) {}
>>>>>>>> +
>>>>>>>> +void test() {
>>>>>>>> +  (void)&A::operator int;
>>>>>>>> +  auto a = &A::operator int;
>>>>>>>> +  deduce(&A::operator int);
>>>>>>>> +
>>>>>>>> +  (void)&B::operator int;
>>>>>>>> +  auto b = &B::operator int;
>>>>>>>> +  deduce(&B::operator int);
>>>>>>>> +
>>>>>>>> +  (void)&C::operator int;
>>>>>>>> +  auto c = &C::operator int;
>>>>>>>> +  deduce(&C::operator int);
>>>>>>>> +
>>>>>>>> +  // (void)&D::operator int D::*; // TODO
>>>>>>>> +  auto d = &D::operator int D::*;
>>>>>>>> +  deduce(&D::operator int D::*);
>>>>>>>> +  deduce2(&D::operator int D::*);
>>>>>>>> +}
>>>>>>>>
>>>>>>>> base-commit: b1987874feead5e98f1ea005bd1ce5ff515eda7a
>>>>>>>
>>>>>>
>>>>>
>>>>
>>>
>>
> 

Reply via email to