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.

>>  
>>    if (TREE_CODE (arg) == TEMPLATE_ID_EXPR)
>>      {
>> @@ -25361,7 +25377,17 @@ resolve_nondeduced_context (tree orig_expr, 
>> tsubst_flags_t complain)
>>        baselink = expr;
>>        expr = BASELINK_FUNCTIONS (expr);
>>      }
>> -
>> +  if (TREE_CODE (expr) == OVERLOAD
>> +      && IDENTIFIER_CONV_OP_P (OVL_NAME (expr)))
>> +    {
>> +      tree tmpl = OVL_FIRST (expr);
>> +      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))
>> +    expr = lookup_template_function (tmpl, targs);
>> +    }
>>    if (TREE_CODE (expr) == TEMPLATE_ID_EXPR)
>>      {
>>        int good = 0;
>> diff --git a/gcc/testsuite/g++.dg/template/conv22.C 
>> b/gcc/testsuite/g++.dg/template/conv22.C
>> new file mode 100644
>> index 00000000000..2e703880192
>> --- /dev/null
>> +++ b/gcc/testsuite/g++.dg/template/conv22.C
>> @@ -0,0 +1,28 @@
>> +// PR c++/122383
>> +// { dg-do compile }
>> +
>> +struct A {
>> +  template<typename T>
>> +  operator T() {
>> +    return 0;
>> +  }
>> +
>> +  template<typename T, typename U>
>> +  operator T U::*() {
>> +    return 0;
>> +  }
>> +};
>> +
>> +template<typename T>
>> +void f(T(A::*)()) {}
>> +
>> +template<typename T, typename U>
>> +void f(T U::*(A::*)()) {}
>> +
>> +int main() {
>> +  &A::operator int;
>> +  &A::operator int A::*;
>> +
>> +  f(&A::operator int);
>> +  f(&A::operator int A::*);
>> +}
>>
>> base-commit: b1987874feead5e98f1ea005bd1ce5ff515eda7a
>> -- 
>> 2.54.0
>>
>>

Reply via email to