From patchwork Mon Sep 12 21:00:26 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dodji Seketeli X-Patchwork-Id: 114419 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]) by ozlabs.org (Postfix) with SMTP id 1C1C8B6F85 for ; Tue, 13 Sep 2011 07:00:56 +1000 (EST) Received: (qmail 12307 invoked by alias); 12 Sep 2011 21:00:52 -0000 Received: (qmail 12298 invoked by uid 22791); 12 Sep 2011 21:00:49 -0000 X-SWARE-Spam-Status: No, hits=-6.2 required=5.0 tests=AWL, BAYES_00, RCVD_IN_DNSWL_HI, RP_MATCHES_RCVD, SPF_HELO_PASS X-Spam-Check-By: sourceware.org Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Mon, 12 Sep 2011 21:00:30 +0000 Received: from int-mx09.intmail.prod.int.phx2.redhat.com (int-mx09.intmail.prod.int.phx2.redhat.com [10.5.11.22]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id p8CL0TS8008659 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK) for ; Mon, 12 Sep 2011 17:00:30 -0400 Received: from localhost (ovpn-113-40.phx2.redhat.com [10.3.113.40]) by int-mx09.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id p8CL0ScC006897 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO); Mon, 12 Sep 2011 17:00:29 -0400 Received: by localhost (Postfix, from userid 500) id 058CA29C10E; Mon, 12 Sep 2011 23:00:26 +0200 (CEST) From: Dodji Seketeli To: Jason Merrill Cc: GCC Patches Subject: [PATCH] c++/48320 - Template parameter packs cannot be expanded in X-URL: http://www.redhat.com Date: Mon, 12 Sep 2011 23:00:26 +0200 Message-ID: User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/23.2 (gnu/linux) MIME-Version: 1.0 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 Hello, fixup_template_parms fixes up template parameters by substituting the result of current_template_args into each template parameter, from left to right. In the example the patch, when the compiler comes to the point of substituting into the default argument of template parameter Result, it is exposed to a subtlety that is twofold. 1/ The parameter pack Indices is now represented by a TEMPLATE_PARM_INDEX, as a result of substituting into the PARM_DECL that was representing it prior to going through fixup_template_parm. 2/ The non-type template parameter Indices, when passed to template_parm_to_arg (indirectly called by current_template_args) is wrapped into an argument pack containing the pack expansion of Indices. Normally, the wrapped form of Indices shouldn't escape tsubst_pack_expansion; this function should detect it, figure out that we are basically substituting the parameter pack Indices into itself and do the right thing. The problem in this PR is that 1/ makes it so that what the representation of Indices that is wrapped in 2/ is not a PARM_DECL but rather a TEMPLATE_PARM_INDEX. But the code of tsubst_pack_expansion was expecting a PARM_DECL in that wrapped form. The patch below factorizes out the code (that detects the wrapped form) from tsubst_pack_expansion, and expands it a little bit to make it be aware of 1/ and 2/ combined. Bootstrapped and tested on x86_64-unknown-linux-gnu against trunk. I'll launch a bootstrap & test on 4.6 as well. From: Dodji Seketeli Date: Mon, 12 Sep 2011 20:17:33 +0200 Subject: [PATCH] c++/48320 - Template parameter packs cannot be expanded in default template arguments gcc/cp/ PR c++/48320 * pt.c (template_parameter_pack_p): Support TEMPLATE_PARM_INDEX nodes. Add a comment. (arg_from_parm_pack_p): New static function, factorized out from tsubst_pack_expansion and extended to support non-type parameter packs represented with TEMPLATE_PARM_INDEX nodes. (tsubst_pack_expansion): Use arg_from_parm_pack_p. gcc/testsuite/ PR c++/48320 * g++.dg/cpp0x/variadic116.C: New test case. --- gcc/cp/pt.c | 115 ++++++++++++++++++------------ gcc/testsuite/g++.dg/cpp0x/variadic116.C | 32 ++++++++ 2 files changed, 100 insertions(+), 47 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp0x/variadic116.C diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index 9a5e3dd..1407364 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -203,6 +203,7 @@ static void append_type_to_template_for_access_check_1 (tree, tree, tree, static tree listify (tree); static tree listify_autos (tree, tree); static tree template_parm_to_arg (tree t); +static bool arg_from_parm_pack_p (tree, tree); static tree current_template_args (void); static tree fixup_template_type_parm_type (tree, int); static tree fixup_template_parm_index (tree, tree, int); @@ -2741,12 +2742,15 @@ template_parameter_pack_p (const_tree parm) if (TREE_CODE (parm) == PARM_DECL) return (DECL_TEMPLATE_PARM_P (parm) && TEMPLATE_PARM_PARAMETER_PACK (DECL_INITIAL (parm))); + if (TREE_CODE (parm) == TEMPLATE_PARM_INDEX) + return TEMPLATE_PARM_PARAMETER_PACK (parm); /* If this is a list of template parameters, we could get a TYPE_DECL or a TEMPLATE_DECL. */ if (TREE_CODE (parm) == TYPE_DECL || TREE_CODE (parm) == TEMPLATE_DECL) parm = TREE_TYPE (parm); + /* Otherwise it must be a type template parameter. */ return ((TREE_CODE (parm) == TEMPLATE_TYPE_PARM || TREE_CODE (parm) == TEMPLATE_TEMPLATE_PARM) && TEMPLATE_TYPE_PARAMETER_PACK (parm)); @@ -4005,6 +4009,63 @@ template_parm_to_arg (tree t) return t; } +/* This function returns TRUE if PARM_PACK is a template parameter + pack and if ARG_PACK is what template_parm_to_arg returned when + passed PARM_PACK. */ + +static bool +arg_from_parm_pack_p (tree arg_pack, tree parm_pack) +{ + /* For clarity in the comments below let's use the representation + argument_pack' to denote an argument pack and its + elements. + + In the 'if' block below, we want to detect cases where + ARG_PACK is argument_pack. I.e, we want to + check if ARG_PACK is an argument pack which sole element is + the expansion of PARM_PACK. That argument pack is typically + created by template_parm_to_arg when passed a parameter + pack. */ + + if (arg_pack + && TREE_VEC_LENGTH (ARGUMENT_PACK_ARGS (arg_pack)) == 1 + && PACK_EXPANSION_P (TREE_VEC_ELT (ARGUMENT_PACK_ARGS (arg_pack), 0))) + { + tree expansion = TREE_VEC_ELT (ARGUMENT_PACK_ARGS (arg_pack), 0); + tree pattern = PACK_EXPANSION_PATTERN (expansion); + /* So we have an argument_pack. We want to test if P + is actually PARM_PACK. We will not use cp_tree_equal to + test P and PARM_PACK because during type fixup (by + fixup_template_parm) P can be a pre-fixup version of a + type and PARM_PACK be its post-fixup version. + cp_tree_equal would consider them as different even + though we would want to consider them compatible for our + precise purpose here. + + Thus we are going to consider that P and PARM_PACK are + compatible if they have the same DECL. */ + if ((/* If ARG_PACK is a type parameter pack named by the + same DECL as parm_pack ... */ + (TYPE_P (pattern) + && TYPE_P (parm_pack) + && TYPE_NAME (pattern) == TYPE_NAME (parm_pack)) + /* ... or if PARM_PACK is a non-type parameter named by the + same DECL as ARG_PACK. Note that PARM_PACK being a + non-type parameter means it's either a PARM_DECL or a + TEMPLATE_PARM_INDEX. */ + || (TREE_CODE (pattern) == TEMPLATE_PARM_INDEX + && ((TREE_CODE (parm_pack) == PARM_DECL + && (TEMPLATE_PARM_DECL (pattern) + == TEMPLATE_PARM_DECL (DECL_INITIAL (parm_pack)))) + || (TREE_CODE (parm_pack) == TEMPLATE_PARM_INDEX + && (TEMPLATE_PARM_DECL (pattern) + == TEMPLATE_PARM_DECL (parm_pack)))))) + && template_parameter_pack_p (pattern)) + return true; + } + return false; +} + /* Within the declaration of a template, return all levels of template parameters that apply. The template parameters are represented as a TREE_VEC, in the form documented in cp-tree.h for template @@ -9105,53 +9166,13 @@ tsubst_pack_expansion (tree t, tree args, tsubst_flags_t complain, return result; } - /* For clarity in the comments below let's use the - representation 'argument_pack' to denote an - argument pack and its elements. - - In the 'if' block below, we want to detect cases where - ARG_PACK is argument_pack. I.e, we want to - check if ARG_PACK is an argument pack which sole element is - the expansion of PARM_PACK. That argument pack is typically - created by template_parm_to_arg when passed a parameter - pack. */ - if (arg_pack - && TREE_VEC_LENGTH (ARGUMENT_PACK_ARGS (arg_pack)) == 1 - && PACK_EXPANSION_P (TREE_VEC_ELT (ARGUMENT_PACK_ARGS (arg_pack), 0))) - { - tree expansion = TREE_VEC_ELT (ARGUMENT_PACK_ARGS (arg_pack), 0); - tree pattern = PACK_EXPANSION_PATTERN (expansion); - /* So we have an argument_pack. We want to test if P - is actually PARM_PACK. We will not use cp_tree_equal to - test P and PARM_PACK because during type fixup (by - fixup_template_parm) P can be a pre-fixup version of a - type and PARM_PACK be its post-fixup version. - cp_tree_equal would consider them as different even - though we would want to consider them compatible for our - precise purpose here. - - Thus we are going to consider that P and PARM_PACK are - compatible if they have the same DECL. */ - if ((/* If ARG_PACK is a type parameter pack named by the - same DECL as parm_pack ... */ - (TYPE_P (pattern) - && TYPE_P (parm_pack) - && TYPE_NAME (pattern) == TYPE_NAME (parm_pack)) - /* ... or if ARG_PACK is a non-type parameter - named by the same DECL as parm_pack ... */ - || (TREE_CODE (pattern) == TEMPLATE_PARM_INDEX - && TREE_CODE (parm_pack) == PARM_DECL - && TEMPLATE_PARM_DECL (pattern) - == TEMPLATE_PARM_DECL (DECL_INITIAL (parm_pack)))) - && template_parameter_pack_p (pattern)) - /* ... then the argument pack that the parameter maps to - is just an expansion of the parameter itself, such as - one would find in the implicit typedef of a class - inside the class itself. Consider this parameter - "unsubstituted", so that we will maintain the outer - pack expansion. */ - arg_pack = NULL_TREE; - } + if (arg_from_parm_pack_p (arg_pack, parm_pack)) + /* The argument pack that the parameter maps to is just an + expansion of the parameter itself, such as one would find + in the implicit typedef of a class inside the class itself. + Consider this parameter "unsubstituted", so that we will + maintain the outer pack expansion. */ + arg_pack = NULL_TREE; if (arg_pack) { diff --git a/gcc/testsuite/g++.dg/cpp0x/variadic116.C b/gcc/testsuite/g++.dg/cpp0x/variadic116.C new file mode 100644 index 0000000..079d751 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/variadic116.C @@ -0,0 +1,32 @@ +// Origin: PR c++/48320 +// { dg-options -std=c++0x } + +template +struct tuple +{ + typedef int type; +}; + +template +struct indices +{ +}; + +template +struct tuple_element +{ + typedef Tuple type; +}; + +template::type...> > +Result +f(Tuple&&, indices); + + +void +foo() +{ + f(tuple (), indices<2, 1, 0> ()); +}