This patch fixes the issue where we do not fail when, during
substitution, we end up creating a single-dimension array parameter of
type void, or an array parameter that has a zero or negative major
bound.  This issue occurs because we decay an array parameter type into
a pointer type as soon as we process the parameter (in grokdeclarator),
so we lose the array type's possibly dependent bounds and so on.

The obvious fix for this issue is to withhold decaying dependent array
parameter types until after substitution is done on them (and to
temporarily decay array parameter types for unification).  That is the
approach that this patch takes.

So first off, in grokdeclarator we just have to retain the array
parameter type if it's dependent (and avoid messing with the element
type's cv qualifiers in grokparms).

On the substitution side of things, nothing extra needs to be done as
tsubst_arg_types already takes care to decay the array parameter type
after substitution.  Things get slightly more tricky on the unification
side.

For unification, we need to add a special case to unify a dependent T[N]
parameter type with a T * argument type so that type deduction, template
function overload resolution, specializations, etc still work properly
in such cases.  After a couple of failed approaches that attempted to
localize the requisite changes to a single common function, e.g. in
unify() or in unify_one_arg() or in maybe_adjust_types_for_deduction() I
realized that the interface for unification is not used consistently
enough to trivially achieve this. For example, a change to unify() will
not work well because one of its callers, more_specialized_fn(), strips
REFERENCE_TYPEs before calling it so we may be inadvertently unifying a
T (&)[N] with a T *& which seems wrong.  So instead, this patch
takes the easier route and just adds preparatory logic to decay these
dependent array parameter types where necessary so that by the time
unify() is called it will be looking at two decayed T * types.  There
only seem to be three places where this needs to be done.

Aside from the two tests derived from the relevant PRs, the new tests
unify12.C - unify16.C are all examples for which my earlier iterations
of this patch introduced regressions.  They run cleanly with or without
this patch.

Tested via bootstrap + regtest on x86_64-pc-linux-gnu, and also tested by
compiling boost and running its testsuite.  Although boost's testsuite
is somewhat spotty, from what I can tell no new regressions were
introduced.

gcc/cp/ChangeLog:

        PR c++/11858
        PR c++/24663
        PR c++/24664
        * decl.c (grokdeclarator): Don't decay array parameter type to
        a pointer type if it's dependent.
        (grokparms): Invoke strip_top_quals instead of directly invoking
        cp_build_qualified_type.
        * pt.c (decay_dependent_array_parm_type): New static function.
        (type_unification_real): Call decay_dependent_array_parm_type
        to decay a dependent array parameter type to its corresponding
        pointer type before unification.
        (more_specialized_fn): Likewise.
        (get_bindings): Likewise.
        * tree.c (cp_build_qualified_type): Trivial typofix in
        documentation.

gcc/testsuite/ChangeLog:

        PR c++/11858
        PR c++/24663
        PR c++/24664
        * g++.dg/template/pr11858.C: New test.
        * g++.dg/template/pr24663.C: New test.
        * g++.dg/template/unify12.C: New test.
        * g++.dg/template/unify13.C: New test.
        * g++.dg/template/unify14.C: New test.
        * g++.dg/template/unify15.C: New test.
        * g++.dg/template/unify16.C: New test.
---
 gcc/cp/decl.c                           | 12 +++++--
 gcc/cp/pt.c                             | 27 +++++++++++++++-
 gcc/cp/tree.c                           |  2 +-
 gcc/testsuite/g++.dg/template/pr11858.C |  5 +++
 gcc/testsuite/g++.dg/template/pr24663.C | 22 +++++++++++++
 gcc/testsuite/g++.dg/template/unify12.C | 46 +++++++++++++++++++++++++++
 gcc/testsuite/g++.dg/template/unify13.C | 26 +++++++++++++++
 gcc/testsuite/g++.dg/template/unify14.C |  5 +++
 gcc/testsuite/g++.dg/template/unify15.C | 15 +++++++++
 gcc/testsuite/g++.dg/template/unify16.C | 56 +++++++++++++++++++++++++++++++++
 10 files changed, 211 insertions(+), 5 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/template/pr11858.C
 create mode 100644 gcc/testsuite/g++.dg/template/pr24663.C
 create mode 100644 gcc/testsuite/g++.dg/template/unify12.C
 create mode 100644 gcc/testsuite/g++.dg/template/unify13.C
 create mode 100644 gcc/testsuite/g++.dg/template/unify14.C
 create mode 100644 gcc/testsuite/g++.dg/template/unify15.C
 create mode 100644 gcc/testsuite/g++.dg/template/unify16.C

diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index af5f265..1c7dfe6 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -10898,8 +10898,13 @@ grokdeclarator (const cp_declarator *declarator,
 
       if (TREE_CODE (type) == ARRAY_TYPE)
        {
-         /* Transfer const-ness of array into that of type pointed to.  */
-         type = build_pointer_type (TREE_TYPE (type));
+         /* Withhold decaying a dependent array type so that that during
+            instantiation we can detect type deduction failure cases such as
+            creating an array of void, creating a zero-size array, etc.  */
+         if (dependent_type_p (type))
+           ;
+         else
+           type = build_pointer_type (TREE_TYPE (type));
          type_quals = TYPE_UNQUALIFIED;
          array_parameter_p = true;
        }
@@ -11696,7 +11701,8 @@ grokparms (tree parmlist, tree *parms)
 
          /* Top-level qualifiers on the parameters are
             ignored for function types.  */
-         type = cp_build_qualified_type (type, 0);
+         type = strip_top_quals (type);
+
          if (TREE_CODE (type) == METHOD_TYPE)
            {
              error ("parameter %qD invalidly declared method type", decl);
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index dab15bd..35b017e 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -17726,6 +17726,23 @@ fn_type_unification (tree fn,
   return r;
 }
 
+/* TYPE is the type of a function parameter.  If TYPE is a (dependent)
+   ARRAY_TYPE, return the corresponding POINTER_TYPE to which it decays.
+   Otherwise return TYPE.  (We shouldn't see non-dependent ARRAY_TYPE
+   parameters because they get decayed as soon as they are declared.)  */
+
+static tree
+decay_dependent_array_parm_type (tree type)
+{
+  if (TREE_CODE (type) == ARRAY_TYPE)
+    {
+      gcc_assert (uses_template_parms (type));
+      return type_decays_to (type);
+    }
+
+  return type;
+}
+
 /* Adjust types before performing type deduction, as described in
    [temp.deduct.call] and [temp.deduct.conv].  The rules in these two
    sections are symmetric.  PARM is the type of a function parameter
@@ -18164,6 +18181,8 @@ type_unification_real (tree tparms,
       arg = args[ia];
       ++ia;
 
+      parm = decay_dependent_array_parm_type (parm);
+
       if (unify_one_argument (tparms, targs, parm, arg, subr, strict,
                              explain_p))
        return 1;
@@ -20166,6 +20185,9 @@ more_specialized_fn (tree pat1, tree pat2, int len)
           len = 0;
         }
 
+      arg1 = decay_dependent_array_parm_type (arg1);
+      arg2 = decay_dependent_array_parm_type (arg2);
+
       if (TREE_CODE (arg1) == REFERENCE_TYPE)
        {
          ref1 = TYPE_REF_IS_RVALUE (arg1) + 1;
@@ -20451,7 +20473,10 @@ get_bindings (tree fn, tree decl, tree explicit_args, 
bool check_rettype)
   for (arg = decl_arg_types, ix = 0;
        arg != NULL_TREE && arg != void_list_node;
        arg = TREE_CHAIN (arg), ++ix)
-    args[ix] = TREE_VALUE (arg);
+    {
+      args[ix] = TREE_VALUE (arg);
+      args[ix] = decay_dependent_array_parm_type (args[ix]);
+    }
 
   if (fn_type_unification (fn, explicit_args, targs,
                           args, ix,
diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
index 250fe27..8e3dbce 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -1012,7 +1012,7 @@ c_build_qualified_type (tree type, int type_quals, tree 
/* orig_qual_type */,
    arrays correctly.  In particular, if TYPE is an array of T's, and
    TYPE_QUALS is non-empty, returns an array of qualified T's.
 
-   FLAGS determines how to deal with ill-formed qualifications. If
+   COMPLAIN determines how to deal with ill-formed qualifications. If
    tf_ignore_bad_quals is set, then bad qualifications are dropped
    (this is permitted if TYPE was introduced via a typedef or template
    type parameter). If bad qualifications are dropped and tf_warning
diff --git a/gcc/testsuite/g++.dg/template/pr11858.C 
b/gcc/testsuite/g++.dg/template/pr11858.C
new file mode 100644
index 0000000..dc0d688
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/pr11858.C
@@ -0,0 +1,5 @@
+// PR c++/11858
+
+template <typename T> struct S { static typename T::x f (); }; // { dg-error 
"" }
+template <class T> int f (int [sizeof(T::f())]);
+int const i = f<S<int> >(0); // { dg-error "no matching function" }
diff --git a/gcc/testsuite/g++.dg/template/pr24663.C 
b/gcc/testsuite/g++.dg/template/pr24663.C
new file mode 100644
index 0000000..2dc68c2
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/pr24663.C
@@ -0,0 +1,22 @@
+// PR c++/24663
+
+template<int I> int f1 (char[I]);
+template<int I> int f1 (char p1 = I);
+int i = f1<0>(0);
+
+template<typename T, int I> int f2 (T[I]); // { dg-error "" }
+int j = f2<int, 0>(0); // { dg-error "no matching function" }
+int k = f2<void, 1>(0); // { dg-error "no matching function" }
+
+int o[5];
+int l = f2<int[5], 1>(&o);
+
+template<int I> int f3 (char [][I]);
+template<int I> int f3 (char p1 = I);
+int x1 = f3<1>(0); // { dg-error "is ambiguous" }
+int x2 = f3<1>();
+
+template<typename T, int I> int f4 (T [][I]); // { dg-error "" }
+int y1 = f4<void, 1>(0); // { dg-error "no matching function" }
+int y2 = f4<int (void), 1>(0); // { dg-error "no matching function" }
+int y3 = f4<int&, 1>(0); // { dg-error "no matching function" }
diff --git a/gcc/testsuite/g++.dg/template/unify12.C 
b/gcc/testsuite/g++.dg/template/unify12.C
new file mode 100644
index 0000000..6e624e4
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/unify12.C
@@ -0,0 +1,46 @@
+// { dg-do run }
+#include <cassert>
+
+template<typename T, int I> int foo (T [I][I]) { return 0; }
+
+template int foo (char [][6]);
+
+template <typename T>
+int foo (T *)
+{
+  return -1;
+}
+
+template <typename T>
+int foo (T [3][3])
+{
+  return 1;
+}
+
+template <int I>
+int foo (bool [I][I])
+{
+  return 2;
+}
+
+template <>
+int foo (bool [3][2])
+{
+  return 3;
+}
+
+char x[3];
+bool y[4];
+bool z[3][2];
+
+int a = foo (&x);
+int b = foo (&y);
+int c = foo (z);
+
+int
+main ()
+{
+  assert (a == 1);
+  assert (b == 2);
+  assert (c == 3);
+}
diff --git a/gcc/testsuite/g++.dg/template/unify13.C 
b/gcc/testsuite/g++.dg/template/unify13.C
new file mode 100644
index 0000000..56a46df
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/unify13.C
@@ -0,0 +1,26 @@
+// { dg-do run }
+#include <cassert>
+
+template<typename T, int I> int foo (T [I][I]) { return 0; }
+
+template<typename T>
+int foo (T [3][2])
+{
+  return 1;
+}
+
+template <>
+int foo (bool [3][2])
+{
+  return 2;
+}
+
+bool z[3][2];
+
+int a = foo (z);
+
+int
+main ()
+{
+  assert (a == 2);
+}
diff --git a/gcc/testsuite/g++.dg/template/unify14.C 
b/gcc/testsuite/g++.dg/template/unify14.C
new file mode 100644
index 0000000..7fda8fd
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/unify14.C
@@ -0,0 +1,5 @@
+template <typename T, int X>
+void bar (T [X]) { }
+
+template <typename T, int X>
+void bar (const T [X]) { }
diff --git a/gcc/testsuite/g++.dg/template/unify15.C 
b/gcc/testsuite/g++.dg/template/unify15.C
new file mode 100644
index 0000000..fe4848b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/unify15.C
@@ -0,0 +1,15 @@
+// { dg-do run }
+#include <cassert>
+
+template <typename T, int N>
+int bar (T (&) [N]) { return 0; }
+
+template <typename T, int N>
+int bar (const T (&) [N]) { return 1; }
+
+int
+main ()
+{
+  const int s[2] = { 0 };
+  assert (bar (s) == 1);
+}
diff --git a/gcc/testsuite/g++.dg/template/unify16.C 
b/gcc/testsuite/g++.dg/template/unify16.C
new file mode 100644
index 0000000..7b5a2aa
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/unify16.C
@@ -0,0 +1,56 @@
+// { dg-do run }
+#include <cassert>
+
+template <typename T>
+struct Foo
+{
+  static int foo (T) { return 0; }
+};
+
+template <typename T, int I>
+struct Foo<T[I]>
+{
+  static int foo (T[I]) { return 1; }
+};
+
+template <int I>
+struct Foo<char[I]>
+{
+  static int foo (char[I]) { return 2; }
+};
+
+template <typename T>
+struct Foo<T[5]>
+{
+  static int foo (T[5]) { return 3; }
+};
+
+template <>
+struct Foo<char[5]>
+{
+  static int foo (char[5]) { return 4; }
+};
+
+template <>
+struct Foo<const char[5]>
+{
+  static int foo (const char[5]) { return 5; }
+};
+
+int a = Foo<const char[5]>::foo (0);
+int b = Foo<char[5]>::foo (0);
+int c = Foo<bool[5]>::foo (0);
+int d = Foo<char[4]>::foo (0);
+int e = Foo<bool[4]>::foo (0);
+int f = Foo<char[]>::foo (0);
+
+int
+main (void)
+{
+  assert (a == 5);
+  assert (b == 4);
+  assert (c == 3);
+  assert (d == 2);
+  assert (e == 1);
+  assert (f == 0);
+}
-- 
2.7.0.rc1.98.gacf58d0.dirty

Reply via email to