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