c++: array cv-quals and template specialization [PR101402]

PRs 101402, 102033, etc. demonstrated that the fix for PR92010 wasn't
handling all cases of the CWG1001/1322 issue with parameter type qual
stripping and arrays with templates.  The problem turned out to be in
determine_specialization, which did an extra substitution without the 92010
fix and then complained that the result didn't match.

But just removing that wrong/redundant code meant that we were accepting
specializations with different numbers of parameters, because the code in
fn_type_unification that compares types in this case wasn't checking for
length mismatch.

After fixing that, I realized that fn_type_unification couldn't tell the
difference between variadic and non-variadic function types, because the
args array doesn't include the terminal void we use to indicate non-variadic
function type.  So I added it, and made the necessary adjustments.

Thanks to qingzhe "nick" huang <nickhuang99@hotmail.com> for the patch that
led me to dig more into this, and the extensive testcases.

	PR c++/51851
	PR c++/101402
	PR c++/102033
	PR c++/102034
	PR c++/102039
	PR c++/102044

gcc/cp/ChangeLog:

	* pt.c (determine_specialization): Remove redundant code.
	(fn_type_unification): Check for mismatched length.
	(type_unification_real): Ignore terminal void.
	(get_bindings): Don't stop at void_list_node.
	* class.c (resolve_address_of_overloaded_function): Likewise.

gcc/testsuite/ChangeLog:

	* g++.dg/template/fnspec2.C: New test.
	* g++.dg/template/parm-cv1.C: New test.
	* g++.dg/template/parm-cv2.C: New test.
	* g++.dg/template/parm-cv3.C: New test.
diff --git a/gcc/cp/class.c b/gcc/cp/class.c
index 5961162..f16e50b 100644
--- a/gcc/cp/class.c
+++ b/gcc/cp/class.c
@@ -8382,7 +8382,7 @@
       nargs = list_length (target_arg_types);
       args = XALLOCAVEC (tree, nargs);
       for (arg = target_arg_types, ia = 0;
-	   arg != NULL_TREE && arg != void_list_node;
+	   arg != NULL_TREE;
 	   arg = TREE_CHAIN (arg), ++ia)
 	args[ia] = TREE_VALUE (arg);
       nargs = ia;
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 009fe6d..287cf4c 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -2230,7 +2230,6 @@
 	{
 	  tree decl_arg_types;
 	  tree fn_arg_types;
-	  tree insttype;
 
 	  /* In case of explicit specialization, we need to check if
 	     the number of template headers appearing in the specialization
@@ -2356,20 +2355,6 @@
 	       template argument.  */
 	    continue;
 
-          /* Remove, from the set of candidates, all those functions
-             whose constraints are not satisfied. */
-          if (flag_concepts && !constraints_satisfied_p (fn, targs))
-            continue;
-
-          // Then, try to form the new function type.
-	  insttype = tsubst (TREE_TYPE (fn), targs, tf_fndecl_type, NULL_TREE);
-	  if (insttype == error_mark_node)
-	    continue;
-	  fn_arg_types
-	    = skip_artificial_parms_for (fn, TYPE_ARG_TYPES (insttype));
-	  if (!compparms (fn_arg_types, decl_arg_types))
-	    continue;
-
 	  /* Save this template, and the arguments deduced.  */
 	  templates = tree_cons (targs, fn, templates);
 	}
@@ -21862,6 +21847,15 @@
 				 TREE_VALUE (sarg));
 	    goto fail;
 	  }
+      if ((i < nargs || sarg)
+	  /* add_candidates uses DEDUCE_EXACT for x.operator foo(), but args
+	     doesn't contain the trailing void, and conv fns are always ().  */
+	  && !DECL_CONV_FN_P (decl))
+	{
+	  unsigned nsargs = i + list_length (sarg);
+	  unify_arity (explain_p, nargs, nsargs);
+	  goto fail;
+	}
     }
 
   /* After doing deduction with the inherited constructor, actually return an
@@ -22385,6 +22379,10 @@
   args = xargs;
   nargs = xnargs;
 
+  /* Only fn_type_unification cares about terminal void.  */
+  if (nargs && args[nargs-1] == void_type_node)
+    --nargs;
+
   ia = 0;
   while (parms && parms != void_list_node
 	 && ia < nargs)
@@ -24886,7 +24884,7 @@
   nargs = list_length (decl_arg_types);
   args = XALLOCAVEC (tree, nargs);
   for (arg = decl_arg_types, ix = 0;
-       arg != NULL_TREE && arg != void_list_node;
+       arg != NULL_TREE;
        arg = TREE_CHAIN (arg), ++ix)
     args[ix] = TREE_VALUE (arg);
 
diff --git a/gcc/testsuite/g++.dg/template/fnspec2.C b/gcc/testsuite/g++.dg/template/fnspec2.C
new file mode 100644
index 0000000..7a4b101
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/fnspec2.C
@@ -0,0 +1,9 @@
+template <class T>
+void f(T);
+
+template<> void f(int, ...);	// { dg-error "match" }
+
+template <class T>
+void g(T, ...);
+
+template<> void g(int);		// { dg-error "match" }
diff --git a/gcc/testsuite/g++.dg/template/parm-cv1.C b/gcc/testsuite/g++.dg/template/parm-cv1.C
new file mode 100644
index 0000000..2677992
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/parm-cv1.C
@@ -0,0 +1,15 @@
+// CWG 1001
+
+template<class T> struct A {
+  typedef T arr[3];
+};
+
+template<class T> void f(const typename A<T>::arr) { } // #1
+
+template void f<int>(const A<int>::arr);
+
+template <class T> struct B {
+  void g(T);
+};
+
+template <class T> void B<T>::g(const T) { } // #2
diff --git a/gcc/testsuite/g++.dg/template/parm-cv2.C b/gcc/testsuite/g++.dg/template/parm-cv2.C
new file mode 100644
index 0000000..cd40e86
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/parm-cv2.C
@@ -0,0 +1,23 @@
+// PR c++/51851
+
+template<class T>
+struct A
+{
+  typedef double Point[2];
+  virtual double calculate(const Point point) const = 0;
+};
+
+template<class T>
+struct B : public A<T>
+{
+  virtual double calculate(const typename A<T>::Point point) const
+  {
+    return point[0];
+  }
+};
+
+int main()
+{
+  B<int> b;
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/template/parm-cv3.C b/gcc/testsuite/g++.dg/template/parm-cv3.C
new file mode 100644
index 0000000..1b69c3b2
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/parm-cv3.C
@@ -0,0 +1,142 @@
+// CWG 1001/1322
+
+// PR c++/101402
+// PR c++/102033
+// PR c++/102034
+// PR c++/102039
+// PR c++/102044
+
+namespace test2{
+template <class T>
+void f(const T);
+
+template<>
+void f<int[]>(const int*){}
+}
+
+namespace test3{
+template <class T>
+struct A{
+void f(T);
+};
+
+template<class T>
+void A<T>::f(const T){}
+
+template<>
+void A<int[3]>::f(const int*){}
+}
+
+namespace test4 {
+template<class TA>
+struct A{
+  template<class TB>
+  struct B{
+    typedef TB Arr3[3];
+  };
+};
+template<class TA, class TB>
+void f(const typename A<TA>::template B<TB>::Arr3){}
+template <>
+void f<int, char>(const typename A<int>::B<char>::Arr3){}
+}
+
+namespace test5
+{
+struct A{
+  typedef int Arr3[3];
+};
+
+template<class T>
+void f(const typename T::Arr3){}
+
+template<>
+void f<A>(const int[3]){}
+}
+
+namespace test6
+{
+struct A{
+  typedef int Arr3[3];
+};
+template<class T>
+void f(const typename T::Arr3){}
+template<>
+void f<A>(const int*){}
+}
+
+#if __cpp_alias_templates
+namespace test7
+{
+template<class TA>
+struct A{
+  template<class TB>
+  using Type=TB[3];
+};
+template<class TA, class TB>
+void f(const typename A<TA>::template Type<TB>){}
+template <>
+void f<int, char>(const typename A<int>::template Type<char>){}
+}
+namespace test8
+{
+template<class TA>
+struct A{
+  template<class TB>
+  struct B{
+    using TB_Alias=TB;
+    template<class TC=TB_Alias>
+    struct C{
+      typedef TC Arr3[3];
+    };
+  };
+};
+template<class TA, class TB>
+void f(const typename A<TA>::template B<TB>::template C<>::Arr3){}
+template <>
+void f<int, char>(const typename A<int>::template B<char>::template C<>::Arr3){}
+}
+#endif
+
+#if __cpp_decltype
+namespace test0
+{
+template <class T>
+struct A{
+  T arr3[3];
+};
+template <class T>
+void f(const decltype(A<T>::arr3)){}
+template <>
+void f<int>(const int[3]){}
+}
+
+#if __cpp_variable_templates
+namespace test9
+{
+template<unsigned int N, class T>
+void f(const T[N]){}
+
+template<unsigned int N, class T>
+using fPtr=decltype(f<N,T>)*;
+
+template<unsigned int N, class T>
+fPtr<N,T> af[N]={&f<N,T>};
+
+template<unsigned int N, class T>
+void g(const decltype(af<N,T>)){}
+
+template<>
+void g<1,int>(const fPtr<1,int>[1]){}
+}
+#endif
+#endif
+
+#if __cpp_concepts
+template<class T>
+concept IsLambdaAry3=__is_same(T, decltype(+[]{})[3]);
+template<IsLambdaAry3 T>
+void bar(const T){}
+template<>
+void bar<decltype(+[]{})[3]>(const decltype(+[]{})[3]){}
+#endif