On 6/10/26 20:01, 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. I agree it is a good idea to ignore 
>> this for now. Is there an existing bug report?
>>
>> 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.
> 
> I'm afraid this patch is too large to go in under the "legally insignificant" 
> exception we've been using for previous patches of yours; any news on that 
> FSF copyright assignment?

I suspected as much. I will start that now.

> 
>> gcc/testsuite/ChangeLog:
>>
>>     * g++.dg/template/conv22.C: New test.
>> ---
>>   gcc/cp/pt.cc                           | 121 +++++++++++++++++--------
>>   gcc/testsuite/g++.dg/template/conv22.C |  69 ++++++++++++++
>>   2 files changed, 151 insertions(+), 39 deletions(-)
>>   create mode 100644 gcc/testsuite/g++.dg/template/conv22.C
>>
>> diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
>> index 6992b5196fe..912bb6b5185 100644
>> --- a/gcc/cp/pt.cc
>> +++ b/gcc/cp/pt.cc
>> @@ -25174,6 +25174,31 @@ type_unification_real (tree tparms,
>>     return unify_success (explain_p);
>>   }
>>   +/* Subroutine of resolve_overloaded_unification and
>> +   resolve_nondeduced_context.  FNS is a conversion operator overload set 
>> and
>> +   OPTYPE is the target type.  Returns an overload set containing the
>> +   instantiated functions that match the target type.  */
>> +
>> +tree
>> +resolve_conversion_function_id (tree fns, tree optype)
>> +{
>> +  tree overloads = NULL_TREE;
>> +  for (lkp_iterator iter (fns); iter; ++iter)
>> +    {
>> +      tree tmpl = *iter;
>> +      tree tparms = DECL_INNERMOST_TEMPLATE_PARMS (tmpl);
>> +      tree targs = make_tree_vec (DECL_NTPARMS (tmpl));
>> +      tree parm = DECL_CONV_FN_TYPE (DECL_TEMPLATE_RESULT (tmpl));
>> +      if (unify (tparms, targs, parm, optype, UNIFY_ALLOW_NONE, false))
>> +    continue;
>> +      tree fn = instantiate_template (tmpl, targs, tf_none);
>> +      if (!constraints_satisfied_p (fn))
>> +    continue;
>> +      overloads = lookup_add (fn, overloads);
>> +    }
>> +  return overloads;
>> +}
>> +
>>   /* Subroutine of type_unification_real.  Args are like the variables
>>      at the call site.  ARG is an overloaded function (or template-id);
>>      we try deducing template args from each of the overloads, and if
>> @@ -25193,6 +25218,7 @@ resolve_overloaded_unification (tree tparms,
>>     int good = 0;
>>     tree goodfn = NULL_TREE;
>>     bool addr_p;
>> +  tree optype = NULL_TREE;
>>       if (TREE_CODE (arg) == ADDR_EXPR)
>>       {
>> @@ -25212,7 +25238,10 @@ resolve_overloaded_unification (tree tparms,
>>       /* Strip baselink information.  */
>>     if (BASELINK_P (arg))
>> -    arg = BASELINK_FUNCTIONS (arg);
>> +    {
>> +      optype = BASELINK_OPTYPE (arg);
>> +      arg = BASELINK_FUNCTIONS (arg);
>> +    }
>>       if (TREE_CODE (arg) == TEMPLATE_ID_EXPR)
>>       {
>> @@ -25276,22 +25305,26 @@ resolve_overloaded_unification (tree tparms,
>>          not just the function on its own.  */
>>       return false;
>>     else
>> -    for (lkp_iterator iter (arg); iter; ++iter)
>> -      {
>> -    tree fn = *iter;
>> -    if (flag_noexcept_type)
>> -      maybe_instantiate_noexcept (fn, tf_none);
>> -    if (TREE_CODE (fn) == FUNCTION_DECL && !constraints_satisfied_p (fn))
>> -      continue;
>> -    tree elem = TREE_TYPE (fn);
>> -    if (try_one_overload (tparms, targs, tempargs, parm, elem,
>> -                  strict, sub_strict, addr_p, explain_p)
>> -        && (!goodfn || !same_type_p (goodfn, elem)))
>> -      {
>> -        goodfn = elem;
>> -        ++good;
>> -      }
>> -      }
>> +    {
>> +      if (optype)
>> +    arg = resolve_conversion_function_id (arg, optype);
>> +      for (lkp_iterator iter (arg); iter; ++iter)
>> +    {
>> +      tree fn = *iter;
>> +      if (flag_noexcept_type)
>> +        maybe_instantiate_noexcept (fn, tf_none);
>> +      if (TREE_CODE (fn) == FUNCTION_DECL && !constraints_satisfied_p (fn))
>> +        continue;
>> +      tree elem = TREE_TYPE (fn);
>> +      if (try_one_overload (tparms, targs, tempargs, parm, elem,
>> +                    strict, sub_strict, addr_p, explain_p)
>> +          && (!goodfn || !same_type_p (goodfn, elem)))
>> +        {
>> +          goodfn = elem;
>> +          ++good;
>> +        }
>> +    }
>> +    }
>>       /* [temp.deduct.type] A template-argument can be deduced from a pointer
>>        to function or pointer to member function argument if the set of
>> @@ -25335,7 +25368,7 @@ resolve_overloaded_unification (tree tparms,
>>   tree
>>   resolve_nondeduced_context (tree orig_expr, tsubst_flags_t complain)
>>   {
>> -  tree expr, offset, baselink;
>> +  tree expr, offset, baselink, optype;
>>     bool addr;
>>       if (!type_unknown_p (orig_expr))
>> @@ -25345,6 +25378,7 @@ resolve_nondeduced_context (tree orig_expr, 
>> tsubst_flags_t complain)
>>     addr = false;
>>     offset = NULL_TREE;
>>     baselink = NULL_TREE;
>> +  optype = NULL_TREE;
>>       if (TREE_CODE (expr) == ADDR_EXPR)
>>       {
>> @@ -25359,14 +25393,14 @@ resolve_nondeduced_context (tree orig_expr, 
>> tsubst_flags_t complain)
>>     if (BASELINK_P (expr))
>>       {
>>         baselink = expr;
>> +      optype = BASELINK_OPTYPE (expr);
>>         expr = BASELINK_FUNCTIONS (expr);
>>       }
>>   +  int good = 0;
>> +  tree goodfn = NULL_TREE;
>>     if (TREE_CODE (expr) == TEMPLATE_ID_EXPR)
>>       {
>> -      int good = 0;
>> -      tree goodfn = NULL_TREE;
>> -
>>         /* If we got some explicit template args, we need to plug them into
>>        the affected templates before we try to unify, in case the
>>        explicit args will completely resolve the templates in question.  */
>> @@ -25403,28 +25437,37 @@ resolve_nondeduced_context (tree orig_expr, 
>> tsubst_flags_t complain)
>>           }
>>           }
>>       }
>> -      if (good == 1)
>> -    {
>> -      expr = goodfn;
>> -      if (baselink)
>> -        expr = build_baselink (BASELINK_BINFO (baselink),
>> -                   BASELINK_ACCESS_BINFO (baselink),
>> -                   expr, BASELINK_OPTYPE (baselink));
>> -      if (offset)
>> -        {
>> -          tree base
>> -        = TYPE_MAIN_VARIANT (TREE_TYPE (TREE_OPERAND (offset, 0)));
>> -          expr = build_offset_ref (base, expr, addr, complain);
>> -        }
>> -      if (addr)
>> -        expr = cp_build_addr_expr (expr, complain);
>> -      return expr;
>> -    }
>> -      else if (good == 0 && badargs && (complain & tf_error))
>> +      if (good == 0 && badargs && (complain & tf_error))
>>       /* There were no good options and at least one bad one, so let the
>>          user know what the problem is.  */
>>       instantiate_template (badfn, badargs, complain);
>>       }
>> +  else if (optype)
>> +    {
>> +      expr = resolve_conversion_function_id (expr, optype);
>> +      if (OVL_SINGLE_P (expr))
>> +    {
>> +      goodfn = OVL_FIRST (expr);
>> +      good = 1;
>> +    }
>> +    }
>> +  if (good == 1)
>> +    {
>> +      expr = goodfn;
>> +      if (baselink)
>> +    expr = build_baselink (BASELINK_BINFO (baselink),
>> +                   BASELINK_ACCESS_BINFO (baselink),
>> +                   expr, BASELINK_OPTYPE (baselink));
>> +      if (offset)
>> +    {
>> +      tree base
>> +        = TYPE_MAIN_VARIANT (TREE_TYPE (TREE_OPERAND (offset, 0)));
>> +      expr = build_offset_ref (base, expr, addr, complain);
>> +    }
>> +      if (addr)
>> +    expr = cp_build_addr_expr (expr, complain);
>> +      return expr;
>> +    }
>>     return orig_expr;
>>   }
>>   diff --git a/gcc/testsuite/g++.dg/template/conv22.C 
>> b/gcc/testsuite/g++.dg/template/conv22.C
>> new file mode 100644
>> index 00000000000..7cc85abc8c3
>> --- /dev/null
>> +++ b/gcc/testsuite/g++.dg/template/conv22.C
>> @@ -0,0 +1,69 @@
>> +// PR c++/122383
>> +// { dg-do compile { target c++20 } }
>> +
>> +struct A {
>> +  template<typename T>
>> +  operator T() {
>> +    return T();
>> +  }
>> +};
>> +
>> +struct B {
>> +  operator int() {
>> +    return 0;
>> +  }
>> +
>> +  template<typename T>
>> +  operator T() {
>> +    static_assert(false);
>> +  }
>> +};
>> +
>> +struct C {
>> +  template<typename T>
>> +  operator T() {
>> +    return T();
>> +  }
>> +
>> +  template<typename U>
>> +  operator U() requires(false) {
>> +    return U();
>> +  }
>> +};
>> +
>> +struct D {
>> +  template<typename T>
>> +  operator T() {
>> +    static_assert(false);
>> +  }
>> +
>> +  template<typename T, typename U>
>> +  operator T U::*() {
>> +    return 0;
>> +  }
>> +};
>> +
>> +template<typename R, typename C>
>> +void deduce(R(C::*)()) {}
>> +
>> +template<typename R, typename C1, typename C2>
>> +void deduce2(R C1::*(C2::*)()) {}
>> +
>> +void test() {
>> +  (void)&A::operator int;
>> +  auto a = &A::operator int;
>> +  deduce(&A::operator int);
>> +
>> +  (void)&B::operator int;
>> +  auto b = &B::operator int;
>> +  deduce(&B::operator int);
>> +
>> +  (void)&C::operator int;
>> +  auto c = &C::operator int;
>> +  deduce(&C::operator int);
>> +
>> +  // (void)&D::operator int D::*; // TODO
>> +  auto d = &D::operator int D::*;
>> +  deduce(&D::operator int D::*);
>> +  deduce2(&D::operator int D::*);
>> +}
>>
>> base-commit: b1987874feead5e98f1ea005bd1ce5ff515eda7a
> 

Reply via email to