On 6/3/26 10:47 AM, Marek Polacek wrote:
On Wed, Jun 03, 2026 at 08:54:51AM -0400, Jason Merrill wrote:
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

Done.

the convert_nontype_argument function comment that it expects non-dependent
TYPE and non-type-dependent EXPR?  And the existing

We already say that: "Both TYPE and EXPR must be non-dependent."

maybe_convert_nontype_argument should probably be something more distinct
like maybe_build_nontype_implicit_conv.

Done.  Ran dg.exp.  Ok for trunk?

OK, thanks.

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

        * cp-tree.h: Adjust comments to mention
        maybe_build_nontype_implicit_conv instead of
        maybe_convert_nontype_argument.
        * pt.cc (maybe_convert_nontype_argument): Rename to...
        (maybe_build_nontype_implicit_conv): ...this.
        (convert_nontype_argument_maybe_dependent): New, factored
        out of...
        (convert_template_argument): ...here.
        (unify) <case TEMPLATE_PARM_INDEX>: Call
        convert_nontype_argument_maybe_dependent 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/cp-tree.h                         |  4 +-
  gcc/cp/pt.cc                             | 58 +++++++++++++++---------
  gcc/testsuite/g++.dg/template/deduce11.C | 15 ++++++
  gcc/testsuite/g++.dg/template/deduce12.C | 12 +++++
  4 files changed, 65 insertions(+), 24 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/cp-tree.h b/gcc/cp/cp-tree.h
index 65f112714d1..bf477a67a34 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -5138,7 +5138,7 @@ get_vec_init_expr (tree t)
    (TREE_LANG_FLAG_0 (IMPLICIT_CONV_EXPR_CHECK (NODE)))
/* True if NODE represents a dependent conversion of a non-type template
-   argument.  Set by maybe_convert_nontype_argument.  */
+   argument.  Set by maybe_build_nontype_implicit_conv.  */
  #define IMPLICIT_CONV_EXPR_NONTYPE_ARG(NODE) \
    (TREE_LANG_FLAG_1 (IMPLICIT_CONV_EXPR_CHECK (NODE)))
@@ -5148,7 +5148,7 @@ get_vec_init_expr (tree t)
    (TREE_LANG_FLAG_2 (IMPLICIT_CONV_EXPR_CHECK (NODE)))
/* True if NODE represents a conversion forced to be represented in
-   maybe_convert_nontype_argument, i.e. for an alias template.  */
+   maybe_build_nontype_implicit_conv, i.e. for an alias template.  */
  #define IMPLICIT_CONV_EXPR_FORCED(NODE) \
    (TREE_LANG_FLAG_3 (IMPLICIT_CONV_EXPR_CHECK (NODE)))
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index b334882de81..4683b57f34b 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -8740,7 +8740,7 @@ is_compatible_template_arg (tree parm, tree arg, tree 
args)
     conversion for the benefit of cp_tree_equal.  */
static tree
-maybe_convert_nontype_argument (tree type, tree arg, bool force)
+maybe_build_nontype_implicit_conv (tree type, tree arg, bool force)
  {
    /* Auto parms get no conversion.  */
    if (type_uses_auto (type))
@@ -8780,6 +8780,33 @@ dependent_implicit_conv_p (tree type, tree expr, bool 
forced)
              && value_dependent_expression_p (expr)));
  }
+/* Convert the non-type template parameter ARG to the indicated TYPE.
+   If one of them is dependent, create an appropriate conversion.
+   FORCE_CONV is true in a forced context (i.e. alias or concept).  */
+
+static tree
+convert_nontype_argument_maybe_dependent (tree type, tree arg,
+                                         bool force_conv,
+                                         tsubst_flags_t complain)
+{
+  if (dependent_implicit_conv_p (type, arg, force_conv))
+    {
+      tree val = canonicalize_expr_argument (arg, complain);
+      return maybe_build_nontype_implicit_conv (type, val, 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.  */
+  return convert_nontype_argument (type, convert_from_reference (arg),
+                                  complain);
+}
+
  /* Convert the indicated template ARG as necessary to match the
     indicated template PARM.  Returns the converted ARG, or
     error_mark_node if the conversion was unsuccessful.  Error and
@@ -9060,23 +9087,8 @@ convert_template_argument (tree parm,
          && same_type_p (TREE_TYPE (orig_arg), t))
        orig_arg = TREE_OPERAND (orig_arg, 0);
- 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);
-       }
+      val = convert_nontype_argument_maybe_dependent (t, orig_arg, force_conv,
+                                                     complain);
if (val == NULL_TREE)
        val = error_mark_node;
@@ -26567,10 +26579,12 @@ 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);
-      }
+      arg = convert_nontype_argument_maybe_dependent (tparm, arg,
+                                                     /*forced=*/false,
+                                                     complain);
+      if (!arg || arg == error_mark_node)
+       return unify_invalid (explain_p);
+
        TREE_VEC_ELT (INNERMOST_TEMPLATE_ARGS (targs), idx) = arg;
        return unify_success (explain_p);
diff --git a/gcc/testsuite/g++.dg/template/deduce11.C b/gcc/testsuite/g++.dg/template/deduce11.C
new file mode 100644
index 00000000000..c7671c04397
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/deduce11.C
@@ -0,0 +1,15 @@
+// PR c++/107124
+// { dg-do compile { target c++11 } }
+
+template<int>
+struct A {};
+
+template<const int &I>
+constexpr int f (A<I>) { return 0; }
+
+template<typename T>
+constexpr int f (T) { return 1; }
+
+const int a = 42;
+static_assert (f (A<0>{}) == 1, "");
+static_assert (f (A<a>{}) == 1, "");
diff --git a/gcc/testsuite/g++.dg/template/deduce12.C 
b/gcc/testsuite/g++.dg/template/deduce12.C
new file mode 100644
index 00000000000..0bd2762edd4
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/deduce12.C
@@ -0,0 +1,12 @@
+// PR c++/107124
+// { dg-do compile { target c++11 } }
+
+template<int>
+struct A {};
+
+template<const int &I>
+constexpr int f (A<I>) { return 0; }
+
+constexpr int i = f (A<0>{}); // { dg-error "no matching function for call" }
+// { dg-message "(candidate|does not bind directly|could not convert)" "candidate 
note" { target c++17 } .-1 }
+// { dg-message "(candidate|not a valid template argument)" "candidate note" { 
target c++14_down } .-2 }

base-commit: 42989c22cfdbcd4207482bc6476ddb8d00a3424d

Reply via email to