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.

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

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.

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