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.

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