On Friday, September 25th, 2026 at 2:59 AM, Jason Merrill <[email protected]> 
wrote:
> On 9/25/26 3:21 AM, Waffl3x wrote:
> > On Thursday, September 24th, 2026 at 11:07 AM, Jason Merrill 
> > <[email protected]> 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.
> >
> > I told Eczbek to not worry about it for now and wait to see what you
> > say, but I reckon we want to match our behavior described in p3606.
> >
> > struct S
> > {
> >    template <typename T>
> >      requires std::copyable<T>
> >    explicit S(T op) noexcept; // #1
> >    S(const S&) noexcept = default; // #2
> > };
> > static_assert(std::copyable<S>);
> >
> > Where we pick #2 without instantiating #1.
> 
> resolve_address_ already ignores templates after finding a matching
> non-template.  But I don't think we can skip deduction when we don't
> have a target type...
> 
> > What is unclear to me is what a perfect match would be for this,
> > whether noexceptness needs to match as well.
> 
> ...because there can't be a perfect match when there's no target type.
> 
> > The standard also doesn't seem to specify ordering of cases where there
> > is no target type or the target type is a placeholder type. The closest
> > I could find was https://eel.is/c++draft/temp.deduct.partial#3.2 ,
> > which is probably good enough to show it would be in the spirit to do
> > it in this case.
> 
> I think [over.over] covers this:
> 
> * All non-template functions are added to the set (p2)
> * All deducible templates are added to the set -- in the case of no
> target type, this is just from optype or explicit template args (p3)
> * Do partial ordering (p5)

Ah, I missed p5, thank you. [temp.func.order] p8.3 only specifies for a
call to a conversion function, but I still think it's obvious enough it
should also apply to taking the address.

> 
> If there's only one function left, that's the result.
> 
> >>>>>> 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.
> >
> > There seems to be lots of cases where the type would be different where
> > we still want to do this. Explicit object member functions throw
> > another wrench into this.
> 
> We only prefer one on the basis of partial ordering, and since we're
> doing this on the basis of function signature I don't see how it could
> apply when the types are different.  And I don't see the complication
> from xobj functions; the address of such a function has a defined type.
> 
> Jason

I think I see my misunderstanding, this is just for the case without a
target type then? Oh... yeah looking at what you were directly replying
to that is obvious in hindsight, my bad. Thanks for clarifying.

Alex

> > I'm not familiar with instantiate_type at all though so I'm not sure
> > what the actual correct thing here is. It seems if we decide to worry
> > about perfect match it gets rather complex. After pondering it for a
> > bit, it might be a lot more out of reach than I initially thought.
> >
> >>> 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