c++: Fix deduction from the type of an NTTP
authorPatrick Palka <ppalka@redhat.com>
Tue, 5 Jan 2021 18:36:26 +0000 (13:36 -0500)
committerPatrick Palka <ppalka@redhat.com>
Tue, 5 Jan 2021 18:36:26 +0000 (13:36 -0500)
In the testcase nontype-auto17.C below, the calls to f and g are invalid
because neither deduction nor defaulting of the template parameter T
yields a valid specialization.  Deducing T doesn't work because T is
used only in a non-deduced context, and defaulting T doesn't work
because its default argument makes the type of M invalid.

But with -std=c++17 or later, we incorrectly accept both calls.
Starting with C++17 (specifically P0127R2), during deduction we're
allowed to try to deduce T from the argument '42' that's been
tentatively deduced for M.  The problem is that when unify walks into
the type of M (a TYPENAME_TYPE), it immediately gives up without
performing any new unifications (so the type of M is still unknown) --
and then we go on to unify M with '42' anyway.  Later in
type_unification_real, we complete the template argument vector using
T's default template argument, and end up forming the bogus
specializations f<void, 42> and g<S, 42>.

This patch fixes this issue by checking whether the type of an NTTP is
still dependent after walking into its type during unification.  If it
is, it means we couldn't deduce all the template parameters used in its
type, and so we shouldn't yet unify the NTTP.

(The new testcase ttp33.C demonstrates the need for the TEMPLATE_PARM_LEVEL
check; without it, we would ICE on this testcase from the call to tsubst.)

gcc/cp/ChangeLog:

* pt.c (unify) <case TEMPLATE_PARM_INDEX>: After walking into
the type of the NTTP, substitute into the type again.  If the
type is still dependent, don't unify the NTTP.

gcc/testsuite/ChangeLog:

* g++.dg/template/partial5.C: Adjust directives to expect the
same errors across all dialects.
* g++.dg/cpp1z/nontype-auto17.C: New test.
* g++.dg/cpp1z/nontype-auto18.C: New test.
* g++.dg/template/ttp33.C: New test.

gcc/cp/pt.c
gcc/testsuite/g++.dg/cpp1z/nontype-auto17.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp1z/nontype-auto18.C [new file with mode: 0644]
gcc/testsuite/g++.dg/template/partial5.C
gcc/testsuite/g++.dg/template/ttp33.C [new file with mode: 0644]

index 0d061adc2ed214e4c46468fc71a563d24c34b860..beabcc4b027c91737f5394ed7fd630de189a5051 100644 (file)
@@ -23584,13 +23584,21 @@ unify (tree tparms, tree targs, tree parm, tree arg, int strict,
          /* We haven't deduced the type of this parameter yet.  */
          if (cxx_dialect >= cxx17
              /* We deduce from array bounds in try_array_deduction.  */
-             && !(strict & UNIFY_ALLOW_INTEGER))
+             && !(strict & UNIFY_ALLOW_INTEGER)
+             && TEMPLATE_PARM_LEVEL (parm) <= TMPL_ARGS_DEPTH (targs))
            {
              /* Deduce it from the non-type argument.  */
              tree atype = TREE_TYPE (arg);
              RECUR_AND_CHECK_FAILURE (tparms, targs,
                                       tparm, atype,
                                       UNIFY_ALLOW_NONE, explain_p);
+             /* Now check whether the type of this parameter is still
+                dependent, and give up if so.  */
+             ++processing_template_decl;
+             tparm = tsubst (tparm, targs, tf_none, NULL_TREE);
+             --processing_template_decl;
+             if (uses_template_parms (tparm))
+               return unify_success (explain_p);
            }
          else
            /* Try again later.  */
diff --git a/gcc/testsuite/g++.dg/cpp1z/nontype-auto17.C b/gcc/testsuite/g++.dg/cpp1z/nontype-auto17.C
new file mode 100644 (file)
index 0000000..509eb0e
--- /dev/null
@@ -0,0 +1,10 @@
+// { dg-do compile { target c++11 } }
+
+template <int> struct K { };
+
+template <class T = void, typename T::type M> int f(K<M>); // { dg-error "void" }
+int a = f(K<42>{}); // { dg-error "no match" }
+
+struct S { using type = void; };
+template <class T = S, typename T::type M> int g(K<M>); // { dg-message "deduction" }
+int b = g(K<42>{}); // { dg-error "no match" }
diff --git a/gcc/testsuite/g++.dg/cpp1z/nontype-auto18.C b/gcc/testsuite/g++.dg/cpp1z/nontype-auto18.C
new file mode 100644 (file)
index 0000000..936c841
--- /dev/null
@@ -0,0 +1,7 @@
+// { dg-do compile { target c++11 } }
+
+template <int> struct K { };
+
+struct S { using type = int; };
+template <class T = S, typename T::type M> int g(K<M>);
+int a = g(K<42>{});
index a56229770f441486b649430bcdbce633859d0dfd..40d8c45b087954f809ba9045b9d0b573950ae953 100644 (file)
@@ -14,7 +14,7 @@ template<typename T, typename T::foo V>
 struct Y { };
 
 template<typename T, typename U, U v>
-struct Y<T, v> { }; // { dg-error "" "" { target { ! c++17 } } }
+struct Y<T, v> { }; // { dg-error "" }
 
 
 template<typename T, T V>
diff --git a/gcc/testsuite/g++.dg/template/ttp33.C b/gcc/testsuite/g++.dg/template/ttp33.C
new file mode 100644 (file)
index 0000000..cd0de8c
--- /dev/null
@@ -0,0 +1,10 @@
+// A slight variation of ttp31.C.
+// { dg-do compile { target c++11 } }
+
+template<class TA,
+        template<typename TA::type...> class TTA, TA... VA>
+struct A { };
+
+template<class UC, class TC,
+        template<typename TC::type...> class TTC, TC... VC>
+struct C : A<TC, TTC, VC...> { };