c++: array cv-quals and template specialization [PR101402]
authorJason Merrill <jason@redhat.com>
Tue, 28 Sep 2021 14:02:04 +0000 (10:02 -0400)
committerJason Merrill <jason@redhat.com>
Fri, 15 Oct 2021 20:12:56 +0000 (16:12 -0400)
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.

gcc/cp/class.c
gcc/cp/pt.c
gcc/testsuite/g++.dg/template/fnspec2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/template/parm-cv1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/template/parm-cv2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/template/parm-cv3.C [new file with mode: 0644]

index 5961162..f16e50b 100644 (file)
@@ -8382,7 +8382,7 @@ resolve_address_of_overloaded_function (tree target_type,
       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;
index 009fe6d..287cf4c 100644 (file)
@@ -2230,7 +2230,6 @@ determine_specialization (tree template_id,
        {
          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 @@ determine_specialization (tree template_id,
               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 @@ fn_type_unification (tree fn,
                                 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 @@ type_unification_real (tree tparms,
   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 @@ get_bindings (tree fn, tree decl, tree explicit_args, bool check_rettype)
   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 (file)
index 0000000..7a4b101
--- /dev/null
@@ -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 (file)
index 0000000..2677992
--- /dev/null
@@ -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 (file)
index 0000000..cd40e86
--- /dev/null
@@ -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 (file)
index 0000000..1b69c3b
--- /dev/null
@@ -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