From patchwork Sun Oct 2 21:38:57 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jason Merrill X-Patchwork-Id: 117379 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 DCBE7B6F18 for ; Mon, 3 Oct 2011 08:39:33 +1100 (EST) Received: (qmail 27503 invoked by alias); 2 Oct 2011 21:39:31 -0000 Received: (qmail 27491 invoked by uid 22791); 2 Oct 2011 21:39:26 -0000 X-SWARE-Spam-Status: No, hits=-6.7 required=5.0 tests=AWL, BAYES_00, RCVD_IN_DNSWL_HI, RP_MATCHES_RCVD, SPF_HELO_PASS, TW_FN 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; Sun, 02 Oct 2011 21:39:01 +0000 Received: from int-mx12.intmail.prod.int.phx2.redhat.com (int-mx12.intmail.prod.int.phx2.redhat.com [10.5.11.25]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id p92Ld0tu018981 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK) for ; Sun, 2 Oct 2011 17:39:00 -0400 Received: from ns3.rdu.redhat.com (ns3.rdu.redhat.com [10.11.255.199]) by int-mx12.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id p92Lcxvo019735 for ; Sun, 2 Oct 2011 17:38:59 -0400 Received: from [0.0.0.0] (ovpn-113-54.phx2.redhat.com [10.3.113.54]) by ns3.rdu.redhat.com (8.13.8/8.13.8) with ESMTP id p92Lcvh9008420 for ; Sun, 2 Oct 2011 17:38:58 -0400 Message-ID: <4E88D9F1.2070709@redhat.com> Date: Sun, 02 Oct 2011 17:38:57 -0400 From: Jason Merrill User-Agent: Mozilla/5.0 (X11; Linux i686; rv:6.0.2) Gecko/20110906 Thunderbird/6.0.2 MIME-Version: 1.0 To: gcc-patches List Subject: C++ PATCH for c++/35722 (N2555 variadic pack expansion to fixed template parameter set) 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 This patch implements the extension voted in with paper N2555, after the initial implementation of variadic templates in GCC. Under this extension, a template argument list can contain a pack expansion even if the template has a fixed parameter list. For example, template struct foo {}; template struct bar : foo {}; int main() { bar f; } Here the template argument pack specified for bar has two elements, so it ends up matching the parameter list for foo and all is good. My implementation approach was as follows: 1) Don't try to do anything with an unknown or incomplete argument pack, just leave it alone until we know all the arguments. 2) Since we aren't substituting partial packs anymore, when deducing a function parameter pack, plug in explicit template arguments as we go. 3) If we see a template parameter pack used in a function parameter pack not at the end of the parameter list, we can't deduce it, so just plug in the explicit template arguments (if any). This last point is an open area of discussion in the committee, but it gets the job done and seems like a reasonable approach to me. The first patch here is the substantive changes; the second patch is just factoring out common code in type_unification_real and unify_pack_expansion into a separate function. Tested x86_64-pc-linux-gnu, applying to trunk. commit dca90afa40bc6dae2198c18e60bc07334772564d Author: Jason Merrill Date: Tue Sep 27 01:38:51 2011 -0400 PR c++/35722 Implement N2555 (expanding pack expansion to fixed parm list) * pt.c (coerce_template_parms): Allow expanding a pack expansion to a fixed-length argument list. (unify_pack_expansion): Handle explicit args properly. (unify) [TREE_VEC]: Handle pack expansions here. [TYPE_ARGUMENT_PACK]: Not here. (tsubst_pack_expansion): Don't try to do partial substitution. (pack_deducible_p): New. (fn_type_unification): Use it. (find_parameter_packs_r): Take the TYPE_MAIN_VARIANT of a type parameter. (check_non_deducible_conversion): Split out from fn_type_unification. diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index 4d57f94..1b7337e 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -2961,6 +2961,7 @@ find_parameter_packs_r (tree *tp, int *walk_subtrees, void* data) break; case TEMPLATE_TYPE_PARM: + t = TYPE_MAIN_VARIANT (t); case TEMPLATE_TEMPLATE_PARM: if (TEMPLATE_TYPE_PARAMETER_PACK (t)) parameter_pack_p = true; @@ -6741,22 +6742,10 @@ coerce_template_parms (tree parms, { if (PACK_EXPANSION_P (arg)) { - if (complain & tf_error) - { - /* FIXME this restriction was removed by N2555; see - bug 35722. */ - /* If ARG is a pack expansion, but PARM is not a - template parameter pack (if it were, we would have - handled it above), we're trying to expand into a - fixed-length argument list. */ - if (TREE_CODE (arg) == EXPR_PACK_EXPANSION) - sorry ("cannot expand %<%E%> into a fixed-length " - "argument list", arg); - else - sorry ("cannot expand %<%T%> into a fixed-length " - "argument list", arg); - } - ++lost; + /* We don't know how many args we have yet, just + use the unconverted ones for now. */ + new_inner_args = args; + break; } } else if (require_all_args) @@ -9116,7 +9105,6 @@ tsubst_pack_expansion (tree t, tree args, tsubst_flags_t complain, tree pack, packs = NULL_TREE, unsubstituted_packs = NULL_TREE; int i, len = -1; tree result; - int incomplete = 0; htab_t saved_local_specializations = NULL; gcc_assert (PACK_EXPANSION_P (t)); @@ -9189,21 +9177,15 @@ tsubst_pack_expansion (tree t, tree args, tsubst_flags_t complain, int my_len = TREE_VEC_LENGTH (ARGUMENT_PACK_ARGS (arg_pack)); - /* It's all-or-nothing with incomplete argument packs. */ - if (incomplete && !ARGUMENT_PACK_INCOMPLETE_P (arg_pack)) - return error_mark_node; - + /* Don't bother trying to do a partial substitution with + incomplete packs; we'll try again after deduction. */ if (ARGUMENT_PACK_INCOMPLETE_P (arg_pack)) - incomplete = 1; + return t; if (len < 0) len = my_len; else if (len != my_len) { - if (incomplete) - /* We got explicit args for some packs but not others; - do nothing now and try again after deduction. */ - return t; if (TREE_CODE (t) == TYPE_PACK_EXPANSION) error ("mismatched argument pack lengths while expanding " "%<%T%>", @@ -9261,8 +9243,8 @@ tsubst_pack_expansion (tree t, tree args, tsubst_flags_t complain, /* For each argument in each argument pack, substitute into the pattern. */ - result = make_tree_vec (len + incomplete); - for (i = 0; i < len + incomplete; ++i) + result = make_tree_vec (len); + for (i = 0; i < len; ++i) { /* For parameter pack, change the substitution of the parameter pack to the ith argument in its argument pack, then expand @@ -9307,13 +9289,6 @@ tsubst_pack_expansion (tree t, tree args, tsubst_flags_t complain, else TREE_VEC_ELT (result, i) = tsubst (pattern, args, complain, in_decl); - if (i == len) - /* When we have incomplete argument packs, the last "expanded" - result is itself a pack expansion, which allows us - to deduce more arguments. */ - TREE_VEC_ELT (result, i) = - make_pack_expansion (TREE_VEC_ELT (result, i)); - if (TREE_VEC_ELT (result, i) == error_mark_node) { result = error_mark_node; @@ -14289,6 +14264,40 @@ pop_deduction_access_scope (tree tmpl) pop_deferring_access_checks (); } +/* PARM is a template parameter pack for FN. Returns true iff + PARM is used in a deducible way in the argument list of FN. */ + +static bool +pack_deducible_p (tree parm, tree fn) +{ + tree t = FUNCTION_FIRST_USER_PARMTYPE (fn); + for (; t; t = TREE_CHAIN (t)) + { + tree type = TREE_VALUE (t); + tree packs; + if (!PACK_EXPANSION_P (type)) + continue; + for (packs = PACK_EXPANSION_PARAMETER_PACKS (type); + packs; packs = TREE_CHAIN (packs)) + if (TREE_VALUE (packs) == parm) + { + /* The template parameter pack is used in a function parameter + pack. If this is the end of the parameter list, the + template parameter pack is deducible. */ + if (TREE_CHAIN (t) == void_list_node) + return true; + else + /* Otherwise, not. Well, it could be deduced from + a non-pack parameter, but doing so would end up with + a deduction mismatch, so don't bother. */ + return false; + } + } + /* The template parameter pack isn't used in any function parameter + packs, but it might be used deeper, e.g. tuple. */ + return true; +} + /* The FN is a TEMPLATE_DECL for a function. ARGS is an array with NARGS elements of the arguments that are being used when calling it. TARGS is a vector into which the deduced template arguments @@ -14334,7 +14343,6 @@ fn_type_unification (tree fn, tree parms; tree fntype; int result; - bool incomplete_argument_packs_p = false; gcc_assert (TREE_CODE (fn) == TEMPLATE_DECL); @@ -14386,6 +14394,7 @@ fn_type_unification (tree fn, { tree parm = TREE_VALUE (TREE_VEC_ELT (tparms, i)); bool parameter_pack = false; + tree targ = TREE_VEC_ELT (converted_args, i); /* Dig out the actual parm. */ if (TREE_CODE (parm) == TYPE_DECL @@ -14400,16 +14409,15 @@ fn_type_unification (tree fn, parameter_pack = TEMPLATE_PARM_PARAMETER_PACK (parm); } - if (parameter_pack) - { - int level, idx; - tree targ; - template_parm_level_and_index (parm, &level, &idx); + if (!parameter_pack && targ == NULL_TREE) + /* No explicit argument for this template parameter. */ + incomplete = true; + if (parameter_pack && pack_deducible_p (parm, fn)) + { /* Mark the argument pack as "incomplete". We could still deduce more arguments during unification. We remove this mark in type_unification_real. */ - targ = TMPL_ARG (converted_args, level, idx); if (targ) { ARGUMENT_PACK_INCOMPLETE_P(targ) = 1; @@ -14418,18 +14426,10 @@ fn_type_unification (tree fn, } /* We have some incomplete argument packs. */ - incomplete_argument_packs_p = true; + incomplete = true; } } - if (incomplete_argument_packs_p) - /* Any substitution is guaranteed to be incomplete if there - are incomplete argument packs, because we can still deduce - more arguments. */ - incomplete = 1; - else - incomplete = NUM_TMPL_ARGS (explicit_targs) != NUM_TMPL_ARGS (targs); - processing_template_decl += incomplete; fntype = deduction_tsubst_fntype (fn, converted_args, (explain_p @@ -14643,6 +14643,44 @@ maybe_adjust_types_for_deduction (unification_kind_t strict, return result; } +/* Subroutine of type_unification_real and unify_pack_expansion. PARM is a + function parameter of a template which does contain any deducible + template parameters; check if ARG is a suitable match for it. STRICT, + FLAGS and EXPLAIN_P are as in fn_type_unification. */ + +static int +check_non_deducible_conversion (tree parm, tree arg, int strict, + int flags, bool explain_p) +{ + tree type; + + if (!TYPE_P (arg)) + type = TREE_TYPE (arg); + else + type = arg; + + if (same_type_p (parm, type)) + return unify_success (explain_p); + + if (strict == DEDUCE_CONV) + { + if (can_convert_arg (type, parm, NULL_TREE, flags)) + return unify_success (explain_p); + } + else if (strict != DEDUCE_EXACT) + { + if (can_convert_arg (parm, type, + TYPE_P (arg) ? NULL_TREE : arg, + flags)) + return unify_success (explain_p); + } + + if (strict == DEDUCE_EXACT) + return unify_type_mismatch (explain_p, parm, arg); + else + return unify_arg_conversion (explain_p, parm, type, arg); +} + /* Most parms like fn_type_unification. If SUBR is 1, we're being called recursively (to unify the @@ -14739,34 +14777,15 @@ type_unification_real (tree tparms, corresponds with a function parameter that contains only non-deducible template parameters and explicitly specified template parameters. */ + /* FIXME uses_deducible_template_parms */ if (!uses_template_parms (parm)) { - tree type; - - if (!TYPE_P (arg)) - type = TREE_TYPE (arg); + int ret = check_non_deducible_conversion (parm, arg, strict, + flags, explain_p); + if (ret) + return ret; else - type = arg; - - if (same_type_p (parm, type)) continue; - if (strict == DEDUCE_CONV) - { - if (can_convert_arg (type, parm, NULL_TREE, flags)) - continue; - } - else if (strict != DEDUCE_EXACT) - { - if (can_convert_arg (parm, type, - TYPE_P (arg) ? NULL_TREE : arg, - flags)) - continue; - } - - if (strict == DEDUCE_EXACT) - return unify_type_mismatch (explain_p, parm, arg); - else - return unify_arg_conversion (explain_p, parm, type, arg); } if (!TYPE_P (arg)) @@ -15495,21 +15514,64 @@ unify_pack_expansion (tree tparms, tree targs, tree packed_parms, unified and unify each with the pattern. */ for (i = start; i < len; i++) { - tree parm = pattern; + tree parm; + bool any_explicit = false; + tree arg = TREE_VEC_ELT (packed_args, i); - /* For each parameter pack, clear out the deduced value so that - we can deduce it again. */ + /* For each parameter pack, set its TMPL_ARG to either NULL_TREE + or the element of its argument pack at the current index if + this argument was explicitly specified. */ for (pack = packs; pack; pack = TREE_CHAIN (pack)) { int idx, level; + tree arg, pargs; template_parm_level_and_index (TREE_PURPOSE (pack), &level, &idx); - TMPL_ARG (targs, level, idx) = NULL_TREE; + arg = NULL_TREE; + if (TREE_VALUE (pack) + && (pargs = ARGUMENT_PACK_EXPLICIT_ARGS (TREE_VALUE (pack))) + && (i < TREE_VEC_LENGTH (pargs))) + { + any_explicit = true; + arg = TREE_VEC_ELT (pargs, i); + } + TMPL_ARG (targs, level, idx) = arg; } + /* If we had explicit template arguments, substitute them into the + pattern before deduction. */ + if (any_explicit) + { + /* Some arguments might still be unspecified or dependent. */ + bool dependent; + ++processing_template_decl; + dependent = any_dependent_template_arguments_p (targs); + if (!dependent) + --processing_template_decl; + parm = tsubst (pattern, targs, + explain_p ? tf_warning_or_error : tf_none, + NULL_TREE); + if (dependent) + --processing_template_decl; + if (parm == error_mark_node) + return 1; + /* FIXME uses_deducible_template_parms */ + if (!uses_template_parms (parm)) + { + int ret = check_non_deducible_conversion (parm, arg, strict, + LOOKUP_IMPLICIT, + explain_p); + if (ret) + return ret; + else + goto unified; + } + } + else + parm = pattern; + /* Unify the pattern with the current argument. */ { - tree arg = TREE_VEC_ELT (packed_args, i); tree arg_expr = NULL_TREE; int arg_strict = strict; @@ -15619,21 +15681,12 @@ unify_pack_expansion (tree tparms, tree targs, tree packed_parms, if (old_pack && ARGUMENT_PACK_INCOMPLETE_P (old_pack)) { - /* Prepend the explicit arguments onto NEW_ARGS. */ + /* If we had fewer function args than explicit template args, + just use the explicits. */ tree explicit_args = ARGUMENT_PACK_EXPLICIT_ARGS (old_pack); - tree old_args = new_args; - int i, explicit_len = TREE_VEC_LENGTH (explicit_args); - int len = explicit_len + TREE_VEC_LENGTH (old_args); - - /* Copy the explicit arguments. */ - new_args = make_tree_vec (len); - for (i = 0; i < explicit_len; i++) - TREE_VEC_ELT (new_args, i) = TREE_VEC_ELT (explicit_args, i); - - /* Copy the deduced arguments. */ - for (; i < len; i++) - TREE_VEC_ELT (new_args, i) = - TREE_VEC_ELT (old_args, i - explicit_len); + int explicit_len = TREE_VEC_LENGTH (explicit_args); + if (len < explicit_len) + new_args = explicit_args; } if (!old_pack) @@ -16253,17 +16306,56 @@ unify (tree tparms, tree targs, tree parm, tree arg, int strict, case TREE_VEC: { - int i; + int i, len, argslen; + int parm_variadic_p = 0; + if (TREE_CODE (arg) != TREE_VEC) return unify_template_argument_mismatch (explain_p, parm, arg); - if (TREE_VEC_LENGTH (parm) != TREE_VEC_LENGTH (arg)) - return unify_arity (explain_p, TREE_VEC_LENGTH (arg), - TREE_VEC_LENGTH (parm)); - for (i = 0; i < TREE_VEC_LENGTH (parm); ++i) - RECUR_AND_CHECK_FAILURE (tparms, targs, - TREE_VEC_ELT (parm, i), - TREE_VEC_ELT (arg, i), - UNIFY_ALLOW_NONE, explain_p); + + len = TREE_VEC_LENGTH (parm); + argslen = TREE_VEC_LENGTH (arg); + + /* Check for pack expansions in the parameters. */ + for (i = 0; i < len; ++i) + { + if (PACK_EXPANSION_P (TREE_VEC_ELT (parm, i))) + { + if (i == len - 1) + /* We can unify against something with a trailing + parameter pack. */ + parm_variadic_p = 1; + else + /* [temp.deduct.type]/9: If the template argument list of + P contains a pack expansion that is not the last + template argument, the entire template argument list + is a non-deduced context. */ + return unify_success (explain_p); + } + } + + /* If we don't have enough arguments to satisfy the parameters + (not counting the pack expression at the end), or we have + too many arguments for a parameter list that doesn't end in + a pack expression, we can't unify. */ + if (parm_variadic_p + ? argslen < len - parm_variadic_p + : argslen != len) + return unify_arity (explain_p, TREE_VEC_LENGTH (arg), len); + + /* Unify all of the parameters that precede the (optional) + pack expression. */ + for (i = 0; i < len - parm_variadic_p; ++i) + { + RECUR_AND_CHECK_FAILURE (tparms, targs, + TREE_VEC_ELT (parm, i), + TREE_VEC_ELT (arg, i), + UNIFY_ALLOW_NONE, explain_p); + } + if (parm_variadic_p) + return unify_pack_expansion (tparms, targs, parm, arg, + UNIFY_ALLOW_NONE, + /*call_args_p=*/false, + /*subr=*/false, explain_p); return unify_success (explain_p); } @@ -16425,58 +16517,8 @@ unify (tree tparms, tree targs, tree parm, tree arg, int strict, case TYPE_ARGUMENT_PACK: case NONTYPE_ARGUMENT_PACK: - { - tree packed_parms = ARGUMENT_PACK_ARGS (parm); - tree packed_args = ARGUMENT_PACK_ARGS (arg); - int i, len = TREE_VEC_LENGTH (packed_parms); - int argslen = TREE_VEC_LENGTH (packed_args); - int parm_variadic_p = 0; - - for (i = 0; i < len; ++i) - { - if (PACK_EXPANSION_P (TREE_VEC_ELT (packed_parms, i))) - { - if (i == len - 1) - /* We can unify against something with a trailing - parameter pack. */ - parm_variadic_p = 1; - else - /* Since there is something following the pack - expansion, we cannot unify this template argument - list. */ - return unify_success (explain_p); - } - } - - - /* If we don't have enough arguments to satisfy the parameters - (not counting the pack expression at the end), or we have - too many arguments for a parameter list that doesn't end in - a pack expression, we can't unify. */ - if (argslen < (len - parm_variadic_p)) - return unify_too_few_arguments (explain_p, argslen, len); - if (argslen > len && !parm_variadic_p) - return unify_too_many_arguments (explain_p, argslen, len); - - /* Unify all of the parameters that precede the (optional) - pack expression. */ - for (i = 0; i < len - parm_variadic_p; ++i) - { - RECUR_AND_CHECK_FAILURE (tparms, targs, - TREE_VEC_ELT (packed_parms, i), - TREE_VEC_ELT (packed_args, i), - strict, explain_p); - } - - if (parm_variadic_p) - return unify_pack_expansion (tparms, targs, - packed_parms, packed_args, - strict, /*call_args_p=*/false, - /*subr=*/false, explain_p); - return unify_success (explain_p); - } - - break; + return unify (tparms, targs, ARGUMENT_PACK_ARGS (parm), + ARGUMENT_PACK_ARGS (arg), strict, explain_p); case TYPEOF_TYPE: case DECLTYPE_TYPE: diff --git a/gcc/testsuite/g++.dg/cpp0x/variadic-explicit1.C b/gcc/testsuite/g++.dg/cpp0x/variadic-explicit1.C new file mode 100644 index 0000000..a097f43 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/variadic-explicit1.C @@ -0,0 +1,11 @@ +// { dg-options -std=c++0x } + +template struct A { }; +template void f( A... p); + +void g() { + f( + A(), + A() + ); +} diff --git a/gcc/testsuite/g++.dg/cpp0x/variadic-nondeduce1.C b/gcc/testsuite/g++.dg/cpp0x/variadic-nondeduce1.C new file mode 100644 index 0000000..a64d797 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/variadic-nondeduce1.C @@ -0,0 +1,12 @@ +// { dg-options -std=c++0x } + +template +void f(T..., int, T...) { } + +int main() +{ + f(0); + f(0,0,0); + f(0,0,0,0,0); + f(0,0,0); // { dg-error "" } +} diff --git a/gcc/testsuite/g++.dg/cpp0x/variadic105.C b/gcc/testsuite/g++.dg/cpp0x/variadic105.C index 66387b2..66d24a7 100644 --- a/gcc/testsuite/g++.dg/cpp0x/variadic105.C +++ b/gcc/testsuite/g++.dg/cpp0x/variadic105.C @@ -20,6 +20,6 @@ struct call_sum { int main() { // This shouldn't be an error; this is bug 35722. - reverse(1,2); // { dg-bogus "no match" "" { xfail *-*-* } } - // { dg-bogus "sorry, unimplemented" "candidate explanation" { xfail *-*-* } 6 } + reverse(1,2); // { dg-bogus "no match" "" } + // { dg-bogus "sorry, unimplemented" "candidate explanation" { target *-*-* } 6 } } diff --git a/gcc/testsuite/g++.dg/cpp0x/variadic117.C b/gcc/testsuite/g++.dg/cpp0x/variadic117.C new file mode 100644 index 0000000..22f2fc5 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/variadic117.C @@ -0,0 +1,11 @@ +// { dg-options -std=c++0x } + +template struct A { typedef T type; }; + +template