On 6/2/26 6:01 PM, Marek Polacek wrote:
On Fri, May 29, 2026 at 04:37:55PM -0400, Jason Merrill wrote:
On 5/29/26 4:21 PM, Marek Polacek wrote:
On Thu, May 28, 2026 at 03:21:35PM -0400, Jason Merrill wrote:
On 5/28/26 11:42 AM, Marek Polacek wrote:
On Wed, May 27, 2026 at 06:15:04PM -0400, Jason Merrill wrote:
On 5/27/26 4:32 PM, Marek Polacek wrote:
Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?

-- >8 --
[temp.arg.nontype] tells us that a temporary object is not an acceptable
template-argument when the corresponding template parameter has reference
type.  So

      template<const int &> struct B {};
      B<1> b;

is ill-formed.  In the test below we have a tparm `const int &I` and we
are trying to deduce `I` from `A<0>`.  Since a temporary would be required
for the template argument, this should be a deduction failure.

        PR c++/107124

gcc/cp/ChangeLog:

        * pt.cc (unify) <case TEMPLATE_PARM_INDEX>: Return
        unify_type_mismatch if a temporary would be required for the
        template argument.

gcc/testsuite/ChangeLog:

        * g++.dg/template/deduce11.C: New test.
        * g++.dg/template/deduce12.C: New test.
---
     gcc/cp/pt.cc                             |  7 ++++++-
     gcc/testsuite/g++.dg/template/deduce11.C | 15 +++++++++++++++
     gcc/testsuite/g++.dg/template/deduce12.C | 11 +++++++++++
     3 files changed, 32 insertions(+), 1 deletion(-)
     create mode 100644 gcc/testsuite/g++.dg/template/deduce11.C
     create mode 100644 gcc/testsuite/g++.dg/template/deduce12.C

diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index ada5f53a9f1..41b86a4e4f7 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -26485,7 +26485,12 @@ unify (tree tparms, tree targs, tree parm, tree arg, 
int strict,
        ;
           else if (same_type_ignoring_top_level_qualifiers_p
               (non_reference (TREE_TYPE (arg)),
-               non_reference (tparm)))
+               non_reference (tparm))
+              /* A temporary object is not an acceptable template-argument
+                 when the corresponding template parm has reference type.  */
+              && !(TYPE_REF_P (tparm)
+                   && ref_conv_binds_to_temporary
+                      (tparm, convert_from_reference (arg)).is_true ()))

This seems like an awkward place for this check since it isn't a type
mismatch, which leads to the unhelpful diagnostic

           •   mismatched types ‘const int&’ and ‘int’

I think better would be to after checking for pack mismatch, check
invalid_tparm_referent_p, get the diagnostic from there, and return plain
unify_invalid if it fails.

I put it there because it seemed simple to fall back on unify_type_mismatch
(and it's what clang++ says too).  But fair enough; I agree with your point.

I can't use invalid_tparm_referent_p because it wouldn't detect the problem
(it's the first thing I tried).  So I propose this version.

Ah, because arg is still INTEGER_CST.  This suggests that we want the full
convert_nontype_argument, which gives

initializing ‘const int&’ with ‘int’ in converted constant expression does not 
bind directly

then we should be able to drop the separate strip_typedefs_expr.

So like this?  I'm not certain about the forced argument.  But the
dependent_implicit_conv_p is necessary to avoid crashing on dependent
trees.

I think forced false is correct, we shouldn't be dealing with aliases or
concepts here.

-- >8 --
[temp.arg.nontype] tells us that a temporary object is not an acceptable
template-argument when the corresponding template parameter has reference
type.  So

    template<const int& CRI> struct B { };
    B<1> b;

is ill-formed.  In the test below we have a tparm `const int &I` and we
are trying to deduce `I` from `A<0>`.  Since a temporary would be required
for the template argument, this should be a deduction failure.

        PR c++/107124

gcc/cp/ChangeLog:

        * pt.cc (unify) <case TEMPLATE_PARM_INDEX>: Call
        convert_nontype_argument and return unify_invalid if it failed.
        Don't call strip_typedefs_expr.

gcc/testsuite/ChangeLog:

        * g++.dg/template/deduce11.C: New test.
        * g++.dg/template/deduce12.C: New test.
---
   gcc/cp/pt.cc                             | 12 ++++++++----
   gcc/testsuite/g++.dg/template/deduce11.C | 15 +++++++++++++++
   gcc/testsuite/g++.dg/template/deduce12.C | 12 ++++++++++++
   3 files changed, 35 insertions(+), 4 deletions(-)
   create mode 100644 gcc/testsuite/g++.dg/template/deduce11.C
   create mode 100644 gcc/testsuite/g++.dg/template/deduce12.C

diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index ada5f53a9f1..a70a758c61f 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -26530,10 +26530,14 @@ unify (tree tparms, tree targs, tree parm, tree arg, 
int strict,
          && !TEMPLATE_PARM_PARAMETER_PACK (parm))
        return unify_parameter_pack_mismatch (explain_p, parm, arg);
-      {
-       bool removed_attr = false;
-       arg = strip_typedefs_expr (arg, &removed_attr);
-      }
+      if (!dependent_implicit_conv_p (tparm, arg, /*forced=*/false))
+       {
+         arg = convert_from_reference (arg);
+         arg = convert_nontype_argument (tparm, arg, complain);
+         if (!arg || arg == error_mark_node)
+           return unify_invalid (explain_p);
+       }

We probably want the else from convert_template_argument, too:

       if (!dependent_implicit_conv_p (t, orig_arg, force_conv))
         /* We used to call digest_init here.  However, digest_init
will report errors, which we don't want when complain
is zero.  More importantly, digest_init will try too
hard to convert things: for example, `0' should not be
converted to pointer type at this point according to
the standard.  Accepting this is not merely an
extension, since deciding whether or not these
conversions can occur is part of determining which
function template to call, or whether a given explicit
argument specification is valid.  */
         val = convert_nontype_argument (t, orig_arg, complain);
       else
         {
           val = canonicalize_expr_argument (orig_arg, complain);
           val = maybe_convert_nontype_argument (t, val, force_conv);
         }

...maybe factoring that out into another function.

Sounds good, done here.

Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?

-- >8 --
[temp.arg.nontype] tells us that a temporary object is not an acceptable
template-argument when the corresponding template parameter has reference
type.  So

   template<const int& CRI> struct B { };
   B<1> b;

is ill-formed.  In the test below we have a tparm `const int &I` and we
are trying to deduce `I` from `A<0>`.  Since a temporary would be required
for the template argument, this should be a deduction failure.

        PR c++/107124

gcc/cp/ChangeLog:

        * pt.cc (coerce_nontype_argument): New, factored out of...
        (convert_template_argument): ...here.

Hmm, this name adds to the already-confusing group of similarly-named functions. How about convert_nontype_argument_maybe_dependent, and note in the convert_nontype_argument function comment that it expects non-dependent TYPE and non-type-dependent EXPR? And the existing maybe_convert_nontype_argument should probably be something more distinct like maybe_build_nontype_implicit_conv.

Jason

Reply via email to