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