c++: Fix mismatch in template argument deduction [PR90505]

2020-03-03  Jason Merrill  <jason@redhat.com>
	    Marek Polacek  <polacek@redhat.com>

	PR c++/90505 - mismatch in template argument deduction.
	* pt.c (tsubst): Don't reduce the template level of template
	parameters when tf_partial.

	* g++.dg/template/deduce4.C: New test.
	* g++.dg/template/deduce5.C: New test.
	* g++.dg/template/deduce6.C: New test.
	* g++.dg/template/deduce7.C: New test.
diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index 80fb96e..cb5211e 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -1,4 +1,11 @@
 2020-03-04  Jason Merrill  <jason@redhat.com>
+	    Marek Polacek  <polacek@redhat.com>
+
+	PR c++/90505 - mismatch in template argument deduction.
+	* pt.c (tsubst): Don't reduce the template level of template
+	parameters when tf_partial.
+
+2020-03-04  Jason Merrill  <jason@redhat.com>
 
 	PR c++/90432
 	* init.c (perform_member_init): Don't do aggregate initialization of
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index f233e78..2de9036 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -14630,6 +14630,11 @@
 	     about the template parameter in question.  */
 	  return t;
 
+	/* Like with 'auto', don't reduce the level of template parameters
+	   to avoid mismatches when deducing their types.  */
+	if (complain & tf_partial)
+	  return t;
+
 	/* If we get here, we must have been looking at a parm for a
 	   more deeply nested template.  Make a new version of this
 	   template parameter, but with a lower level.  */
diff --git a/gcc/testsuite/g++.dg/template/deduce4.C b/gcc/testsuite/g++.dg/template/deduce4.C
new file mode 100644
index 0000000..e2c165d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/deduce4.C
@@ -0,0 +1,17 @@
+// PR c++/90505 - mismatch in template argument deduction.
+// { dg-do compile }
+
+template <typename T>
+struct S {
+  template <typename U, typename V>
+  static void foo(V) { }
+
+  void bar () { foo<int>(10); }
+};
+
+void
+test ()
+{
+  S<int> s;
+  s.bar ();
+}
diff --git a/gcc/testsuite/g++.dg/template/deduce5.C b/gcc/testsuite/g++.dg/template/deduce5.C
new file mode 100644
index 0000000..9d382bf
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/deduce5.C
@@ -0,0 +1,17 @@
+// PR c++/90505 - mismatch in template argument deduction.
+// { dg-do compile { target c++11 } }
+
+template <typename T>
+struct S {
+  template <typename U, typename V = void>
+  static void foo(U) { }
+
+  void bar () { foo<int>(10); }
+};
+
+void
+test ()
+{
+  S<int> s;
+  s.bar ();
+}
diff --git a/gcc/testsuite/g++.dg/template/deduce6.C b/gcc/testsuite/g++.dg/template/deduce6.C
new file mode 100644
index 0000000..8fee612
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/deduce6.C
@@ -0,0 +1,17 @@
+// PR c++/90505 - mismatch in template argument deduction.
+// { dg-do compile { target c++11 } }
+
+template <typename T>
+struct S {
+  template <typename U = int, typename V>
+  static void foo(V) { }
+
+  void bar () { foo<>(10); }
+};
+
+void
+test ()
+{
+  S<int> s;
+  s.bar ();
+}
diff --git a/gcc/testsuite/g++.dg/template/deduce7.C b/gcc/testsuite/g++.dg/template/deduce7.C
new file mode 100644
index 0000000..fbc28e5
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/deduce7.C
@@ -0,0 +1,10 @@
+// PR c++/90505 - mismatch in template argument deduction.
+// { dg-do compile { target c++11 } }
+
+template <typename> class a {
+  using b = int;
+  using c = int;
+  b d;
+  void e() { g<c>(d); }
+  template <typename... f> static void g(f...);
+};