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.
What is unclear to me is what a perfect match would be for this,
whether noexceptness needs to match as well.
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.
> >>>> 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.
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.
Alex
> > 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
> >>>>
> >>>
> >>
> >
>
>