From patchwork Thu Dec 24 17:57:49 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Patrick Palka X-Patchwork-Id: 560961 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 38DB4140C08 for ; Fri, 25 Dec 2015 04:58:09 +1100 (AEDT) Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=gcc.gnu.org header.i=@gcc.gnu.org header.b=DtqTs5s2; dkim-atps=neutral DomainKey-Signature: a=rsa-sha1; c=nofws; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:from :to:cc:subject:date:message-id; q=dns; s=default; b=D7sgb7mkgWnR My7xQ++9yJmXflK1MwYI9qa8gBrYbi/CKO7gFy6HjHZz7kOjNlaFVH9REFgNv40T rL3H/YO9c2FTVKa4TBsx/mJb7UcZ+bIRf0Nmp/Z4ePgaJ///luIQZoWNNcOMQ4wP tJN8ut03VKbxfQOpt1+aV71ZPek3I0o= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:from :to:cc:subject:date:message-id; s=default; bh=vOYar9Cd5opHOmkpjH RcDFs3Lz0=; b=DtqTs5s2u+yWw6/nj+LxiYQpntub7WQ4mLeClaelr62M5XNE67 yEJ1/zQ5OvsWbBta1QNEFeZWv/3YVh7agyMp0rahGQ+2lUPPn7dnunGl4jHJAbbh R4Zr+8ruAQAouTrfPds14RyKruk38DtGUaoFdJcqAQ4WhND0Dzin54olo= Received: (qmail 40241 invoked by alias); 24 Dec 2015 17:58:02 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org Received: (qmail 39924 invoked by uid 89); 24 Dec 2015 17:57:59 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-2.0 required=5.0 tests=AWL, BAYES_00, RCVD_IN_DNSWL_LOW autolearn=ham version=3.3.2 spammy=sk:array_p, Things, spotty, 1, 15 X-HELO: mail-qg0-f53.google.com Received: from mail-qg0-f53.google.com (HELO mail-qg0-f53.google.com) (209.85.192.53) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with (AES128-GCM-SHA256 encrypted) ESMTPS; Thu, 24 Dec 2015 17:57:57 +0000 Received: by mail-qg0-f53.google.com with SMTP id 6so23036848qgy.1 for ; Thu, 24 Dec 2015 09:57:57 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id; bh=fhzr1uTheLJmHvXY8g9AqtGeyAkAdxWGQzv5C5VUIrY=; b=lkUY7wTJcGzgXesd4dllaW0RNXAklSN9FcCJsOT9k7gK1XzIwnhCEIGhcJM12UYpqg qPsZVnIp8O7E5giFpKPbroRAQs4sQZVRtqAbhXPRlB9RkCWDFx3pj/hq6bZ0nN/Y0DyH 9y9PuB9Gh2TbD5WR2ZSeMnQAkLDFKkesqQtzb+vC9UgIsny/A/aS/8m/VyNky2E+5FEN 6emSomUDlmJyIov1pwTL5LSNdReEonCyZqVR03TSzoCWT+HbSWkQZY48LDt7Jj3BefnA czp/FHu7viscT88l9GgHQ2JQ394KBn60/hL4J3A+4qya0l0cz4wCQ4H39QOOhfe79dzI uitg== X-Gm-Message-State: ALoCoQnWUIl/Mw1EfyR4kNSnZF8FzUGbwPzMNNMQfASxoKFxLJ5x90AeKEe1r2RipwUQ71BgARtn+1cmkA3Mm5xLQNK/rVXJFw== X-Received: by 10.140.93.117 with SMTP id c108mr48896950qge.101.1450979875311; Thu, 24 Dec 2015 09:57:55 -0800 (PST) Received: from localhost.localdomain (ool-4353a8be.dyn.optonline.net. [67.83.168.190]) by smtp.gmail.com with ESMTPSA id v23sm20549862qkv.32.2015.12.24.09.57.54 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Thu, 24 Dec 2015 09:57:54 -0800 (PST) From: Patrick Palka To: gcc-patches@gcc.gnu.org Cc: jason@redhat.com, Patrick Palka Subject: [PATCH] Fix the remaining PR c++/24666 blockers (arrays decay to pointers too early) Date: Thu, 24 Dec 2015 12:57:49 -0500 Message-Id: <1450979869-5575-1-git-send-email-patrick@parcs.ath.cx> 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 struct S { static typename T::x f (); }; // { dg-error "" } +template int f (int [sizeof(T::f())]); +int const i = f >(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 f1 (char[I]); +template int f1 (char p1 = I); +int i = f1<0>(0); + +template int f2 (T[I]); // { dg-error "" } +int j = f2(0); // { dg-error "no matching function" } +int k = f2(0); // { dg-error "no matching function" } + +int o[5]; +int l = f2(&o); + +template int f3 (char [][I]); +template int f3 (char p1 = I); +int x1 = f3<1>(0); // { dg-error "is ambiguous" } +int x2 = f3<1>(); + +template int f4 (T [][I]); // { dg-error "" } +int y1 = f4(0); // { dg-error "no matching function" } +int y2 = f4(0); // { dg-error "no matching function" } +int y3 = f4(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 + +template int foo (T [I][I]) { return 0; } + +template int foo (char [][6]); + +template +int foo (T *) +{ + return -1; +} + +template +int foo (T [3][3]) +{ + return 1; +} + +template +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 + +template int foo (T [I][I]) { return 0; } + +template +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 +void bar (T [X]) { } + +template +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 + +template +int bar (T (&) [N]) { return 0; } + +template +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 + +template +struct Foo +{ + static int foo (T) { return 0; } +}; + +template +struct Foo +{ + static int foo (T[I]) { return 1; } +}; + +template +struct Foo +{ + static int foo (char[I]) { return 2; } +}; + +template +struct Foo +{ + static int foo (T[5]) { return 3; } +}; + +template <> +struct Foo +{ + static int foo (char[5]) { return 4; } +}; + +template <> +struct Foo +{ + static int foo (const char[5]) { return 5; } +}; + +int a = Foo::foo (0); +int b = Foo::foo (0); +int c = Foo::foo (0); +int d = Foo::foo (0); +int e = Foo::foo (0); +int f = Foo::foo (0); + +int +main (void) +{ + assert (a == 5); + assert (b == 4); + assert (c == 3); + assert (d == 2); + assert (e == 1); + assert (f == 0); +}