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