On 2026-09-24 19:06:05, Jason Merrill wrote:
> On 9/23/26 10:12 AM, Eczbek wrote:
>> On 2026-09-22 17:41:12, Jason Merrill wrote:
>>> On 9/22/26 8:19 AM, Eczbek wrote:
>>>> On 2026-09-21 13:05:37, Jason Merrill wrote:
>>>>> On 6/10/26 4:06 PM, Eczbek wrote:
>>>>>> On 6/10/26 15:39, Patrick Palka wrote:
>>>>>>> On Wed, 10 Jun 2026, Eczbek wrote:
>>>>>>>
>>>>>>>> On 6/10/26 09:27, Patrick Palka wrote:
>>>>>>>>> On Tue, 9 Jun 2026, Eczbek wrote:
>>>>>>>>>
>>>>>>>>>> On 6/9/26 10:08, Patrick Palka wrote:
>>>>>>>>>>> On Mon, 8 Jun 2026, Eczbek wrote:
>>>>>>>>>>>
>>>>>>>>>>>> On 6/5/26 11:16, Patrick Palka wrote:
>>>>>>>>>>>>> On Thu, 4 Jun 2026, Eczbek wrote:
>>>>>>>>>>>>>
>>>>>>>>>>>>>> On 6/2/26 16:30, Patrick Palka wrote:
>>>>>>>>>>>>>>> On Mon, 1 Jun 2026, Eczbek wrote:
>>>>>>>>>>>>>>>> On 6/1/26 16:33, Jason Merrill wrote:
>>>>>>>>>>>>>>>>> This assumes a template with a single template parameter; it
>>>>>>>>>>>>>>>>> doesn't work for e.g.
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> struct A {
>>>>>>>>>>>>>>>>> template<typename T, typename U>
>>>>>>>>>>>>>>>>> operator T U::*() {
>>>>>>>>>>>>>>>>> return 0;
>>>>>>>>>>>>>>>>> }
>>>>>>>>>>>>>>>>> };
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> int main() {
>>>>>>>>>>>>>>>>> (void) &A::operator int A::*;
>>>>>>>>>>>>>>>>> }
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> to handle the general case you need to deduce the template
>>>>>>>>>>>>>>>>> arguments.
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> Thanks, I did not consider multiple template parameters. Would
>>>>>>>>>>>>>>>> this be similar to what TEMPLATE_ID_EXPR does below, or is
>>>>>>>>>>>>>>>> there some existing helper that should be used? I'm not sure
>>>>>>>>>>>>>>>> how to do this.
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> There are a couple of entrypoints to deduction, all of which
>>>>>>>>>>>>>>> ultimately
>>>>>>>>>>>>>>> call the main workhorse unify. For simplicity I think we could
>>>>>>>>>>>>>>> get away
>>>>>>>>>>>>>>> with calling unify directly from resolve_nondeduced_context with
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> tparms = DECL_TEMPLATE_PARMS of the conversion function
>>>>>>>>>>>>>>> template
>>>>>>>>>>>>>>> targs = empty TREE_VEC same length as DECL_TEMPLATE_PARMS
>>>>>>>>>>>>>>> parm = return type of the template
>>>>>>>>>>>>>>> arg = BASELINK_OPTYPE
>>>>>>>>>>>>>>> strict = UNIFY_ALLOW_NONE (I think?)
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> If unify succeeds, then 'targs' will contain the deduced
>>>>>>>>>>>>>>> template
>>>>>>>>>>>>>>> arguments that we need to instantiate the conversion function
>>>>>>>>>>>>>>> template
>>>>>>>>>>>>>>> with.
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> So for
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> struct A {
>>>>>>>>>>>>>>> template<class T, class U>
>>>>>>>>>>>>>>> operator B<T*, U&>();
>>>>>>>>>>>>>>> };
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> int main() {
>>>>>>>>>>>>>>> &A::operator B<int*, char&>;
>>>>>>>>>>>>>>> }
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> we need to call unify with (where {} is shorthand for a
>>>>>>>>>>>>>>> TREE_VEC)
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> tparms = {T, U}
>>>>>>>>>>>>>>> targs = {NULL, NULL}
>>>>>>>>>>>>>>> parm = B<T*, U&>
>>>>>>>>>>>>>>> arg = B<int*, char&>
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> which should succeed and fill in targs with {int, char}.
>>>>>>>>>>>>>>> Instantiating
>>>>>>>>>>>>>>> the template with {int, char} yields the correct specialization
>>>>>>>>>>>>>>> operator B<int*, char&>.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> Thank you, I made something work! Bootstrapped/regtested again.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> Jason mentioned that what I wrote in the changelog should be in
>>>>>>>>>>>>>> the main commit message, so what should I write in the changelog
>>>>>>>>>>>>>> instead?
>>>>>>>>>>>>>
>>>>>>>>>>>>> I think Jason's point is that the main commit message generally
>>>>>>>>>>>>> shouldn't be empty, even if it just repeats what you wrote in the
>>>>>>>>>>>>> ChangeLog. Though generally the ChangeLog ought to describe how
>>>>>>>>>>>>> the
>>>>>>>>>>>>> code is changed, and the context/motivation of the change should
>>>>>>>>>>>>> be in
>>>>>>>>>>>>> the main commit message, so there shouldn't be too much overlap.
>>>>>>>>>>>>> Here I'd just write:
>>>>>>>>>>>>>
>>>>>>>>>>>>> (resolve_overloaded_unification): Call unify when
>>>>>>>>>>>>> resolving a
>>>>>>>>>>>>> conversion-function-id.
>>>>>>>>>>>>> (resolve_nondeduced_context): Likewise.
>>>>>>>>>>>>>
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> -- >8 --
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> ---
>>>>>>>>>>>>>> gcc/cp/pt.cc | 30
>>>>>>>>>>>>>> ++++++++++++++++++++++++--
>>>>>>>>>>>>>> gcc/testsuite/g++.dg/template/conv22.C | 28
>>>>>>>>>>>>>> ++++++++++++++++++++++++
>>>>>>>>>>>>>> 2 files changed, 56 insertions(+), 2 deletions(-)
>>>>>>>>>>>>>> create mode 100644 gcc/testsuite/g++.dg/template/conv22.C
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
>>>>>>>>>>>>>> index 6992b5196fe..d914a1c817d 100644
>>>>>>>>>>>>>> --- a/gcc/cp/pt.cc
>>>>>>>>>>>>>> +++ b/gcc/cp/pt.cc
>>>>>>>>>>>>>> @@ -25193,6 +25193,7 @@ resolve_overloaded_unification (tree
>>>>>>>>>>>>>> tparms,
>>>>>>>>>>>>>> int good = 0;
>>>>>>>>>>>>>> tree goodfn = NULL_TREE;
>>>>>>>>>>>>>> bool addr_p;
>>>>>>>>>>>>>> + tree baselink = NULL_TREE;
>>>>>>>>>>>>>> if (TREE_CODE (arg) == ADDR_EXPR)
>>>>>>>>>>>>>> {
>>>>>>>>>>>>>> @@ -25212,7 +25213,22 @@ resolve_overloaded_unification (tree
>>>>>>>>>>>>>> tparms,
>>>>>>>>>>>>>> /* Strip baselink information. */
>>>>>>>>>>>>>> if (BASELINK_P (arg))
>>>>>>>>>>>>>> - arg = BASELINK_FUNCTIONS (arg);
>>>>>>>>>>>>>> + {
>>>>>>>>>>>>>> + baselink = arg;
>>>>>>>>>>>>>> + arg = BASELINK_FUNCTIONS (arg);
>>>>>>>>>>>>>> + }
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>> + if (TREE_CODE (arg) == OVERLOAD
>>>>>>>>>>>>>> + && IDENTIFIER_CONV_OP_P (OVL_NAME (arg)))
>>>>>>>>>>>>>> + {
>>>>>>>>>>>>>> + tree tmpl = OVL_FIRST (arg);
>>>>>>>>>>>>>> + tree tparms = DECL_INNERMOST_TEMPLATE_PARMS (tmpl);
>>>>>>>>>>>>>> + tree targs = make_tree_vec (DECL_NTPARMS (tmpl));
>>>>>>>>>>>>>> + tree parm = DECL_CONV_FN_TYPE (DECL_TEMPLATE_RESULT
>>>>>>>>>>>>>> (tmpl));
>>>>>>>>>>>>>> + tree optype = BASELINK_OPTYPE (baselink);
>>>>>>>>>>>>>> + if (!unify (tparms, targs, parm, optype,
>>>>>>>>>>>>>> UNIFY_ALLOW_NONE, false))
>>>>>>>>>>>>>> + arg = lookup_template_function (tmpl, targs);
>>>>>>>>>>>>>> + }
>>>>>>>>>>>>>
>>>>>>>>>>>>> This needs to be done in a loop over each conversion template in
>>>>>>>>>>>>> the
>>>>>>>>>>>>> overload set because the result of deduction can differ for each
>>>>>>>>>>>>> template:
>>>>>>>>>>>>>
>>>>>>>>>>>>> struct A {
>>>>>>>>>>>>> template<class T> operator T(); // #1
>>>>>>>>>>>>> template<class T> operator B<T>(); // #2
>>>>>>>>>>>>> };
>>>>>>>>>>>>>
>>>>>>>>>>>>> int main() {
>>>>>>>>>>>>> &A::operator B<int>; // targs would be {B<int>} for #1 and
>>>>>>>>>>>>> {int} for #2
>>>>>>>>>>>>> }
>>>>>>>>>>>>>
>>>>>>>>>>>>> And instead of calling lookup_template_function to form a
>>>>>>>>>>>>> template-id, I
>>>>>>>>>>>>> think we need to call instantiate_template to directly instantiate
>>>>>>>>>>>>> each template for which deduction succeeded.
>>>>>>>>>>>>>
>>>>>>>>>>>>> We could factor out all this into a helper
>>>>>>>>>>>>>
>>>>>>>>>>>>> tree resolve_conversion_function_id (tree fns, tree optype)
>>>>>>>>>>>>>
>>>>>>>>>>>>> that takes a conversion operator overload set FNS and target
>>>>>>>>>>>>> type OPTYPE and returns an overload set containing the
>>>>>>>>>>>>> instantiated
>>>>>>>>>>>>> functions that match the target type.
>>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>> Thanks, I have this:
>>>>>>>>>>>>
>>>>>>>>>>>> tree
>>>>>>>>>>>> resolve_conversion_function_id (tree fns, tree optype)
>>>>>>>>>>>> {
>>>>>>>>>>>> tree overloads = NULL_TREE;
>>>>>>>>>>>> for (lkp_iterator iter (fns); iter; ++iter)
>>>>>>>>>>>> {
>>>>>>>>>>>> tree tmpl = *iter;
>>>>>>>>>>>> tree tparms = DECL_INNERMOST_TEMPLATE_PARMS (tmpl);
>>>>>>>>>>>> tree targs = make_tree_vec (DECL_NTPARMS (tmpl));
>>>>>>>>>>>> tree parm = DECL_CONV_FN_TYPE (DECL_TEMPLATE_RESULT
>>>>>>>>>>>> (tmpl));
>>>>>>>>>>>> if (unify (tparms, targs, parm, optype, UNIFY_ALLOW_NONE,
>>>>>>>>>>>> false))
>>>>>>>>>>>> continue;
>>>>>>>>>>>> tree fn = instantiate_template (tmpl, targs, tf_none);
>>>>>>>>>>>> if (!constraints_satisfied_p (fn))
>>>>>>>>>>>> continue;
>>>>>>>>>>>> overloads = lookup_add (fn, overloads);
>>>>>>>>>>>> }
>>>>>>>>>>>> return overloads;
>>>>>>>>>>>> }
>>>>>>>>>>>>
>>>>>>>>>>>> But how can the most "specific" overload be selected from the
>>>>>>>>>>>> returned set? Please advise.
>>>>>>>>>>>
>>>>>>>>>>> That's the partial ordering step of overload resolution, done by
>>>>>>>>>>> the the
>>>>>>>>>>> 'tourney' function, see e.g perform_overload_resolution. But I
>>>>>>>>>>> don't think we
>>>>>>>>>>> want to do do partial ordering here, we should just reject the code
>>>>>>>>>>> if there's
>>>>>>>>>>> more than one viable overload after this step. So the previous
>>>>>>>>>>> example
>>>>>>>>>>>
>>>>>>>>>>> struct A {
>>>>>>>>>>> template<class T> operator T(); // #1
>>>>>>>>>>> template<class T> operator B<T>(); // #2
>>>>>>>>>>> };
>>>>>>>>>>> int main() {
>>>>>>>>>>> &A::operator B<int>; // targs would be {B<int>} for #1 and
>>>>>>>>>>> {int} for #2
>>>>>>>>>>> }
>>>>>>>>>>>
>>>>>>>>>>> should be rejected despite #2 being more specialized than #1.
>>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> That seems incomplete to me. Both Clang and MSVC appear to select
>>>>>>>>>> the most specialized overload: https://godbolt.org/z/q5a755bcK
>>>>>>>>>
>>>>>>>>> Good catch. GCC behaves the same and selects the most specialized
>>>>>>>>> conversion
>>>>>>>>> function if we turn f into a non-template to sidestep template
>>>>>>>>> argument
>>>>>>>>> deduction:
>>>>>>>>>
>>>>>>>>> void f(B<int>(A::*)());
>>>>>>>>> int main() { f(&A::operator B<int>); }
>>>>>>>>>
>>>>>>>>> So the problem is that deduction fails to deduce T=B<int> for the
>>>>>>>>> function
>>>>>>>>> template f. Once we can convince GCC of that then it should work by
>>>>>>>>> virtue of
>>>>>>>>> the non-template f case working.
>>>>>>>>>
>>>>>>>>> The analogous non-conversion-function testcase is:
>>>>>>>>>
>>>>>>>>> template<class>
>>>>>>>>> struct B {};
>>>>>>>>>
>>>>>>>>> struct A {
>>>>>>>>> template<class T> void g(T);
>>>>>>>>> template<class T> void g(T) requires true;
>>>>>>>>> };
>>>>>>>>>
>>>>>>>>> template<class T>
>>>>>>>>> void f(void(A::*)(T));
>>>>>>>>>
>>>>>>>>> int main() { f(&A::g<int>); }
>>>>>>>>>
>>>>>>>>> which GCC does accept by deducing T=int for f, despite multiple
>>>>>>>>> function
>>>>>>>>> templates in the overload set. So the problem is specific to
>>>>>>>>> conversion
>>>>>>>>> functions.
>>>>>>>>>
>>>>>>>>> Using your resolve_conversion_function_id implementation, the
>>>>>>>>> following
>>>>>>>>> change to resolve_overloaded_unification seems to do the right thing
>>>>>>>>> without needing to implement additional "more specialized" logic:
>>>>>>>>>
>>>>>>>>> diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
>>>>>>>>> index 891e89f1d763..3f079457b455 100644
>>>>>>>>> --- a/gcc/cp/pt.cc
>>>>>>>>> +++ b/gcc/cp/pt.cc
>>>>>>>>> @@ -25336,8 +25336,12 @@ resolve_overloaded_unification (tree tparms,
>>>>>>>>> arg = TREE_OPERAND (arg, 1);
>>>>>>>>>
>>>>>>>>> /* Strip baselink information. */
>>>>>>>>> + tree optype = NULL_TREE;
>>>>>>>>> if (BASELINK_P (arg))
>>>>>>>>> + {
>>>>>>>>> + optype = BASELINK_OPTYPE (arg);
>>>>>>>>> arg = BASELINK_FUNCTIONS (arg);
>>>>>>>>> + }
>>>>>>>>>
>>>>>>>>> if (TREE_CODE (arg) == TEMPLATE_ID_EXPR)
>>>>>>>>> {
>>>>>>>>> @@ -25401,6 +25405,9 @@ resolve_overloaded_unification (tree tparms,
>>>>>>>>> not just the function on its own. */
>>>>>>>>> return false;
>>>>>>>>> else
>>>>>>>>> + {
>>>>>>>>> + if (optype)
>>>>>>>>> + arg = resolve_conversion_function_id (arg, optype);
>>>>>>>>> for (lkp_iterator iter (arg); iter; ++iter)
>>>>>>>>> {
>>>>>>>>> tree fn = *iter;
>>>>>>>>> @@ -25417,6 +25424,7 @@ resolve_overloaded_unification (tree tparms,
>>>>>>>>> ++good;
>>>>>>>>> }
>>>>>>>>> }
>>>>>>>>> + }
>>>>>>>>>
>>>>>>>>> /* [temp.deduct.type] A template-argument can be deduced from a
>>>>>>>>> pointer
>>>>>>>>> to function or pointer to member function argument if the
>>>>>>>>> set of
>>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>> Thanks, that seems to work! Here's what I have so far, and my tests.
>>>>>>>> (void)&D::operator int D::*; currently fails because
>>>>>>>> resolve_nondeduced_context only accepts single overloads. How should
>>>>>>>> it also select the most specialized overload?
>>>>>>>
>>>>>>> I'm not sure it should. Clang doesn't accept that code either, right?
>>>>>>> In any case, let's ignore that case for now and treat it as a separate
>>>>>>> issue because we also don't accept the non-convfn version of that code:
>>>>>>>
>>>>>>> template<class>
>>>>>>> struct B {};
>>>>>>>
>>>>>>> struct A {
>>>>>>> template<class T> void g(T);
>>>>>>> template<class T> void g(T) requires true;
>>>>>>> };
>>>>>>>
>>>>>>> template<class T>
>>>>>>> void f(void(A::*)(T));
>>>>>>>
>>>>>>> int main() {
>>>>>>> &A::g<int>;
>>>>>>> };
>>>>>>
>>>>>> Clang doesn't accept, but MSVC does.
>>>>>
>>>>> And EDG. And I think it's clear we should under
>>>>> https://eel.is/c++draft/over.over which does consider the case of no
>>>>> target type.
>>>>>
>>>>>> I agree it is a good idea to ignore this for now. Is there an existing
>>>>>> bug report?
>>>>>
>>>>> I think let's not give up on this so quickly, at least not without
>>>>> analyzing it.
>>>>>
>>>>> It seems that the key difference between this and
>>>>>
>>>>> auto x = &A::g<int>;
>>>>>
>>>>> is that in the declaration, we first go through
>>>>> resolve_nondeduced_context (twice, both times to no effect), then
>>>>> resolve_overloaded_unification to determine the type of x, then
>>>>> resolve_address_of_overloaded_function to resolve which actual function
>>>>> we want.
>>>>>
>>>>> Note that resolve_overloaded_unification is only interested in the type;
>>>>> once we have a type we don't care about the individual overloads. So if
>>>>> we have optype I think we should just unify on that and not bother with
>>>>> resolve_conversion_function_id.
>>>>
>>>> To clarify, does this mean we should directly call unify? If so, I'm not
>>>> sure how to get all of the arguments for it here without looping over the
>>>> overloads.
>>>
>>> I was thinking
>>>
>>> unify (tparms, targs, parm, optype, strict, explain_p)
>>
>> Sorry, I'm not sure how to do this properly. This is what I tried:
>>
>> if (BASELINK_P (arg)
>> && unify (tparms, targs, parm, BASELINK_OPTYPE (arg), strict,
>> explain_p))
>> return false;
>>
>> but this breaks all of the `deduce` tests.
>
> Ah, right, we need a function type, not just the optype. And now it occurs
> to me that the functions might differ in noexcept even if they match
> otherwise. So never mind, let's leave resolve_overloaded_unification as in
> your last patch.
>
>>>>> And then in resolve_nondeduced_context, if we have an optype we can pass
>>>>> that to resolve_address_of_overloaded_function, and again we don't need
>>>>> the new function.
>>>>
>>>> I'm guessing that would look something like this, but I can't figure out
>>>> how to pass explicit_targs:
>>>>
>>>> if (BASELINK_P (expr))
>>>> expr =
>>>> resolve_address_of_overloaded_function (BASELINK_OPTYPE (expr),
>>>> BASELINK_FUNCTIONS (expr),
>>>> complain, true,
>>>> /* explicit_targs? */,
>>>> BASELINK_ACCESS_BINFO (expr));
>>>
>>> explicit_targs would be NULL_TREE in this case...
>>>> I'm also having trouble with this because
>>>> resolve_address_of_overloaded_function is declared in class.cc but I'm in
>>>> pt.cc. Please advise.
>>>
>>> ...but it seems it's better to call instantiate_type rather than resolve_
>>> directly.
>>
>> This is what I tried:
>>
>> if (BASELINK_P (expr))
>> {
>> baselink = expr;
>> expr = instantiate_type (BASELINK_OPTYPE (expr),
>> BASELINK_FUNCTIONS (expr), complain);
>> }
>>
>> but this breaks other tests. `&A::operator int;` results in:
>> error: cannot resolve overloaded function ‘operator T’ based on
>> conversion to type ‘int’
>
> And here we would want to check whether if there are multiple results of
> resolve_conversion_function_id they have the same type, and if so pass that
> type to instantiate_type.
To clarify, do you mean that if resolve_conversion_function_id returns an
overload set with more than one overload, we should iterate over every overload
to check if any two have the same function type? I don't understand the
reasoning behind that and how to use instantiate_type here.
>
>> Another case to consider is selecting the overload based on the qualifiers
>> of the target type: https://godbolt.org/z/aY7Gorrx6
>> I discussed this with Waffl3x and we believe that Clang is correct to select
>> the template overload. My patch doesn't affect this.
>
> They are indeed correct. Normally
> instantiate_type/resolve_address_of_overloaded_function take care of that,
> but presumably they don't consider BASELINK_OPTYPE currently, so perhaps we
> need resolve_conversion_function_id there too.
>
> Jason
>
>>>>> And then to handle the non-conversion example above, we could change the
>>>>> existing loop to handle the case where multiple results have the same
>>>>> type, and use resolve_address_of_overloaded_function in that case as
>>>>> well. Or perhaps replace the existing loop with a call to
>>>>> do_auto_deduction. This could indeed be a follow-up patch, but I'd like
>>>>> to address it while we're thinking about it rather than drop it until
>>>>> some unknown time in the future.
>>>>>
>>>>> After these changes, the first resolve_nondeduced_context should fully
>>>>> resolve the expression, so later ones don't need to do anything. Though
>>>>> I also wonder about removing the two calls to resolve_nondeduced_context
>>>>> in the auto path, since they seem to be redundant.
>>>>>
>>>>> Jason
>>>>>
>>>>>> Bootstrapped/regtested again. Is the ChangeLog OK?
>>>>>>
>>>>>> -- >8 --
>>>>>>
>>>>>> Taking the address of a conversion function template instantiation
>>>>>> incorrectly errors.
>>>>>>
>>>>>> PR c++/122383
>>>>>>
>>>>>> gcc/cp/ChangeLog:
>>>>>>
>>>>>> * pt.cc (resolve_conversion_function_id): Filter a conversion
>>>>>> function overload set using unify.
>>>>>> (resolve_overloaded_unification): Call
>>>>>> resolve_conversion_function_id when resolving
>>>>>> a conversion-function-id.
>>>>>> (resolve_nondeduced_context): Likewise.
>>>>>>
>>>>>> gcc/testsuite/ChangeLog:
>>>>>>
>>>>>> * g++.dg/template/conv22.C: New test.
>>>>>> ---
>>>>>> gcc/cp/pt.cc | 121
>>>>>> +++++++++++++++++--------
>>>>>> gcc/testsuite/g++.dg/template/conv22.C | 69 ++++++++++++++
>>>>>> 2 files changed, 151 insertions(+), 39 deletions(-)
>>>>>> create mode 100644 gcc/testsuite/g++.dg/template/conv22.C
>>>>>>
>>>>>> diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
>>>>>> index 6992b5196fe..912bb6b5185 100644
>>>>>> --- a/gcc/cp/pt.cc
>>>>>> +++ b/gcc/cp/pt.cc
>>>>>> @@ -25174,6 +25174,31 @@ type_unification_real (tree tparms,
>>>>>> return unify_success (explain_p);
>>>>>> }
>>>>>> +/* Subroutine of resolve_overloaded_unification and
>>>>>> + resolve_nondeduced_context. FNS is a conversion operator overload
>>>>>> set and
>>>>>> + OPTYPE is the target type. Returns an overload set containing the
>>>>>> + instantiated functions that match the target type. */
>>>>>> +
>>>>>> +tree
>>>>>> +resolve_conversion_function_id (tree fns, tree optype)
>>>>>> +{
>>>>>> + tree overloads = NULL_TREE;
>>>>>> + for (lkp_iterator iter (fns); iter; ++iter)
>>>>>> + {
>>>>>> + tree tmpl = *iter;
>>>>>> + tree tparms = DECL_INNERMOST_TEMPLATE_PARMS (tmpl);
>>>>>> + tree targs = make_tree_vec (DECL_NTPARMS (tmpl));
>>>>>> + tree parm = DECL_CONV_FN_TYPE (DECL_TEMPLATE_RESULT (tmpl));
>>>>>> + if (unify (tparms, targs, parm, optype, UNIFY_ALLOW_NONE, false))
>>>>>> + continue;
>>>>>> + tree fn = instantiate_template (tmpl, targs, tf_none);
>>>>>> + if (!constraints_satisfied_p (fn))
>>>>>> + continue;
>>>>>> + overloads = lookup_add (fn, overloads);
>>>>>> + }
>>>>>> + return overloads;
>>>>>> +}
>>>>>> +
>>>>>> /* Subroutine of type_unification_real. Args are like the variables
>>>>>> at the call site. ARG is an overloaded function (or template-id);
>>>>>> we try deducing template args from each of the overloads, and if
>>>>>> @@ -25193,6 +25218,7 @@ resolve_overloaded_unification (tree tparms,
>>>>>> int good = 0;
>>>>>> tree goodfn = NULL_TREE;
>>>>>> bool addr_p;
>>>>>> + tree optype = NULL_TREE;
>>>>>> if (TREE_CODE (arg) == ADDR_EXPR)
>>>>>> {
>>>>>> @@ -25212,7 +25238,10 @@ resolve_overloaded_unification (tree tparms,
>>>>>> /* Strip baselink information. */
>>>>>> if (BASELINK_P (arg))
>>>>>> - arg = BASELINK_FUNCTIONS (arg);
>>>>>> + {
>>>>>> + optype = BASELINK_OPTYPE (arg);
>>>>>> + arg = BASELINK_FUNCTIONS (arg);
>>>>>> + }
>>>>>> if (TREE_CODE (arg) == TEMPLATE_ID_EXPR)
>>>>>> {
>>>>>> @@ -25276,22 +25305,26 @@ resolve_overloaded_unification (tree tparms,
>>>>>> not just the function on its own. */
>>>>>> return false;
>>>>>> else
>>>>>> - for (lkp_iterator iter (arg); iter; ++iter)
>>>>>> - {
>>>>>> - tree fn = *iter;
>>>>>> - if (flag_noexcept_type)
>>>>>> - maybe_instantiate_noexcept (fn, tf_none);
>>>>>> - if (TREE_CODE (fn) == FUNCTION_DECL && !constraints_satisfied_p
>>>>>> (fn))
>>>>>> - continue;
>>>>>> - tree elem = TREE_TYPE (fn);
>>>>>> - if (try_one_overload (tparms, targs, tempargs, parm, elem,
>>>>>> - strict, sub_strict, addr_p, explain_p)
>>>>>> - && (!goodfn || !same_type_p (goodfn, elem)))
>>>>>> - {
>>>>>> - goodfn = elem;
>>>>>> - ++good;
>>>>>> - }
>>>>>> - }
>>>>>> + {
>>>>>> + if (optype)
>>>>>> + arg = resolve_conversion_function_id (arg, optype);
>>>>>> + for (lkp_iterator iter (arg); iter; ++iter)
>>>>>> + {
>>>>>> + tree fn = *iter;
>>>>>> + if (flag_noexcept_type)
>>>>>> + maybe_instantiate_noexcept (fn, tf_none);
>>>>>> + if (TREE_CODE (fn) == FUNCTION_DECL && !constraints_satisfied_p
>>>>>> (fn))
>>>>>> + continue;
>>>>>> + tree elem = TREE_TYPE (fn);
>>>>>> + if (try_one_overload (tparms, targs, tempargs, parm, elem,
>>>>>> + strict, sub_strict, addr_p, explain_p)
>>>>>> + && (!goodfn || !same_type_p (goodfn, elem)))
>>>>>> + {
>>>>>> + goodfn = elem;
>>>>>> + ++good;
>>>>>> + }
>>>>>> + }
>>>>>> + }
>>>>>> /* [temp.deduct.type] A template-argument can be deduced from a
>>>>>> pointer
>>>>>> to function or pointer to member function argument if the set of
>>>>>> @@ -25335,7 +25368,7 @@ resolve_overloaded_unification (tree tparms,
>>>>>> tree
>>>>>> resolve_nondeduced_context (tree orig_expr, tsubst_flags_t complain)
>>>>>> {
>>>>>> - tree expr, offset, baselink;
>>>>>> + tree expr, offset, baselink, optype;
>>>>>> bool addr;
>>>>>> if (!type_unknown_p (orig_expr))
>>>>>> @@ -25345,6 +25378,7 @@ resolve_nondeduced_context (tree orig_expr,
>>>>>> tsubst_flags_t complain)
>>>>>> addr = false;
>>>>>> offset = NULL_TREE;
>>>>>> baselink = NULL_TREE;
>>>>>> + optype = NULL_TREE;
>>>>>> if (TREE_CODE (expr) == ADDR_EXPR)
>>>>>> {
>>>>>> @@ -25359,14 +25393,14 @@ resolve_nondeduced_context (tree orig_expr,
>>>>>> tsubst_flags_t complain)
>>>>>> if (BASELINK_P (expr))
>>>>>> {
>>>>>> baselink = expr;
>>>>>> + optype = BASELINK_OPTYPE (expr);
>>>>>> expr = BASELINK_FUNCTIONS (expr);
>>>>>> }
>>>>>> + int good = 0;
>>>>>> + tree goodfn = NULL_TREE;
>>>>>> if (TREE_CODE (expr) == TEMPLATE_ID_EXPR)
>>>>>> {
>>>>>> - int good = 0;
>>>>>> - tree goodfn = NULL_TREE;
>>>>>> -
>>>>>> /* If we got some explicit template args, we need to plug them
>>>>>> into
>>>>>> the affected templates before we try to unify, in case the
>>>>>> explicit args will completely resolve the templates in
>>>>>> question. */
>>>>>> @@ -25403,28 +25437,37 @@ resolve_nondeduced_context (tree orig_expr,
>>>>>> tsubst_flags_t complain)
>>>>>> }
>>>>>> }
>>>>>> }
>>>>>> - if (good == 1)
>>>>>> - {
>>>>>> - expr = goodfn;
>>>>>> - if (baselink)
>>>>>> - expr = build_baselink (BASELINK_BINFO (baselink),
>>>>>> - BASELINK_ACCESS_BINFO (baselink),
>>>>>> - expr, BASELINK_OPTYPE (baselink));
>>>>>> - if (offset)
>>>>>> - {
>>>>>> - tree base
>>>>>> - = TYPE_MAIN_VARIANT (TREE_TYPE (TREE_OPERAND (offset, 0)));
>>>>>> - expr = build_offset_ref (base, expr, addr, complain);
>>>>>> - }
>>>>>> - if (addr)
>>>>>> - expr = cp_build_addr_expr (expr, complain);
>>>>>> - return expr;
>>>>>> - }
>>>>>> - else if (good == 0 && badargs && (complain & tf_error))
>>>>>> + if (good == 0 && badargs && (complain & tf_error))
>>>>>> /* There were no good options and at least one bad one, so let
>>>>>> the
>>>>>> user know what the problem is. */
>>>>>> instantiate_template (badfn, badargs, complain);
>>>>>> }
>>>>>> + else if (optype)
>>>>>> + {
>>>>>> + expr = resolve_conversion_function_id (expr, optype);
>>>>>> + if (OVL_SINGLE_P (expr))
>>>>>> + {
>>>>>> + goodfn = OVL_FIRST (expr);
>>>>>> + good = 1;
>>>>>> + }
>>>>>> + }
>>>>>> + if (good == 1)
>>>>>> + {
>>>>>> + expr = goodfn;
>>>>>> + if (baselink)
>>>>>> + expr = build_baselink (BASELINK_BINFO (baselink),
>>>>>> + BASELINK_ACCESS_BINFO (baselink),
>>>>>> + expr, BASELINK_OPTYPE (baselink));
>>>>>> + if (offset)
>>>>>> + {
>>>>>> + tree base
>>>>>> + = TYPE_MAIN_VARIANT (TREE_TYPE (TREE_OPERAND (offset, 0)));
>>>>>> + expr = build_offset_ref (base, expr, addr, complain);
>>>>>> + }
>>>>>> + if (addr)
>>>>>> + expr = cp_build_addr_expr (expr, complain);
>>>>>> + return expr;
>>>>>> + }
>>>>>> return orig_expr;
>>>>>> }
>>>>>> diff --git a/gcc/testsuite/g++.dg/template/conv22.C
>>>>>> b/gcc/testsuite/g++.dg/template/conv22.C
>>>>>> new file mode 100644
>>>>>> index 00000000000..7cc85abc8c3
>>>>>> --- /dev/null
>>>>>> +++ b/gcc/testsuite/g++.dg/template/conv22.C
>>>>>> @@ -0,0 +1,69 @@
>>>>>> +// PR c++/122383
>>>>>> +// { dg-do compile { target c++20 } }
>>>>>> +
>>>>>> +struct A {
>>>>>> + template<typename T>
>>>>>> + operator T() {
>>>>>> + return T();
>>>>>> + }
>>>>>> +};
>>>>>> +
>>>>>> +struct B {
>>>>>> + operator int() {
>>>>>> + return 0;
>>>>>> + }
>>>>>> +
>>>>>> + template<typename T>
>>>>>> + operator T() {
>>>>>> + static_assert(false);
>>>>>> + }
>>>>>> +};
>>>>>> +
>>>>>> +struct C {
>>>>>> + template<typename T>
>>>>>> + operator T() {
>>>>>> + return T();
>>>>>> + }
>>>>>> +
>>>>>> + template<typename U>
>>>>>> + operator U() requires(false) {
>>>>>> + return U();
>>>>>> + }
>>>>>> +};
>>>>>> +
>>>>>> +struct D {
>>>>>> + template<typename T>
>>>>>> + operator T() {
>>>>>> + static_assert(false);
>>>>>> + }
>>>>>> +
>>>>>> + template<typename T, typename U>
>>>>>> + operator T U::*() {
>>>>>> + return 0;
>>>>>> + }
>>>>>> +};
>>>>>> +
>>>>>> +template<typename R, typename C>
>>>>>> +void deduce(R(C::*)()) {}
>>>>>> +
>>>>>> +template<typename R, typename C1, typename C2>
>>>>>> +void deduce2(R C1::*(C2::*)()) {}
>>>>>> +
>>>>>> +void test() {
>>>>>> + (void)&A::operator int;
>>>>>> + auto a = &A::operator int;
>>>>>> + deduce(&A::operator int);
>>>>>> +
>>>>>> + (void)&B::operator int;
>>>>>> + auto b = &B::operator int;
>>>>>> + deduce(&B::operator int);
>>>>>> +
>>>>>> + (void)&C::operator int;
>>>>>> + auto c = &C::operator int;
>>>>>> + deduce(&C::operator int);
>>>>>> +
>>>>>> + // (void)&D::operator int D::*; // TODO
>>>>>> + auto d = &D::operator int D::*;
>>>>>> + deduce(&D::operator int D::*);
>>>>>> + deduce2(&D::operator int D::*);
>>>>>> +}
>>>>>>
>>>>>> base-commit: b1987874feead5e98f1ea005bd1ce5ff515eda7a
>>>>>
>>>>
>>>
>>
>