Hello, Consider this example:
1 template<class...I> struct List {}; 2 template<int T> struct Z {static const int value = T;}; 3 template<int...T> using LZ = List<Z<T>...>; 4 5 template<class...U> 6 struct F 7 { 8 using N = LZ<U::value...>; //#1 This should amount to List<Z<U::value>...> 9 } 10 11 F<Z<1>, Z<2> >::N A; //#2 which G++ fails to compile, with this error message: test-PR53609-3.cc: In instantiation of 'struct F<Z<1>, Z<2> >': test-PR53609-3.cc:11:15: required from here test-PR53609-3.cc:3:43: error: wrong number of template arguments (2, should be 1) template<int...T> using LZ = List<Z<T>...>; ^ test-PR53609-3.cc:2:24: error: provided for 'template<int T> struct Z' template<int T> struct Z {static const int value = T;}; I think this is because in #1, when we substitute the argument pack {U::value...} into the pack expansion Z<T>..., tsubst_pack_expansion yields Z<U::value...>, instead of Z<U::value>..., so the instantiation of LZ amounts to List<Z<U::value...> >, instead of List<Z<U::value>...>. The idea of this patch is to make tsubst_pack_expansion support substituting an argument pack (into a pack expansion) where one of the arguments (let's call it the Ith argument) is itself a pack expansion P. In that case, the Ith element resulting from the substituting should be a pack expansion P'. The pattern of P' is then the pattern of P into which the pattern of the Ith argument of the argument pack has been substituted. Tested on x86_64-unknown-linux-gnu against trunk. gcc/cp/ * pt.c (real_argument_pack_element_p) (any_non_real_argument_pack_element_p) (arg_pack_select_for_pack_expansion) (set_arg_pack_select_index_for_pack_expansion): New static functions. (has_bare_parameter_packs): Factorized out of ... (check_for_bare_parameter_packs): ... here. (tsubst_pack_expansion): Support substituting an argument pack that contains a pack expansion. gcc/testsuite/ * g++.dg/cpp0x/variadic139.C: New test. * g++.dg/cpp0x/variadic140.C: Likewise. * g++.dg/cpp0x/variadic141.C: Likewise. --- gcc/cp/pt.c | 151 ++++++++++++++++++++++++------ gcc/testsuite/g++.dg/cpp0x/variadic139.C | 14 +++ gcc/testsuite/g++.dg/cpp0x/variadic140.C | 22 +++++ gcc/testsuite/g++.dg/cpp0x/variadic141.C | 22 +++++ 4 files changed, 182 insertions(+), 27 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp0x/variadic139.C create mode 100644 gcc/testsuite/g++.dg/cpp0x/variadic140.C create mode 100644 gcc/testsuite/g++.dg/cpp0x/variadic141.C diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index 16952bf..bcfe83f 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -3310,6 +3310,29 @@ make_pack_expansion (tree arg) return result; } +/* Return NULL_TREE iff T contains *NO* unexpanded parameter packs. + Return the TREE_LIST of unexpanded parameter packs otherwise. */ + +static tree +has_bare_parameter_packs (tree t) +{ + tree parameter_packs = NULL_TREE; + struct find_parameter_pack_data ppd; + + if (!processing_template_decl || !t || t == error_mark_node) + return NULL_TREE; + + if (TREE_CODE (t) == TYPE_DECL) + t = TREE_TYPE (t); + + ppd.parameter_packs = ¶meter_packs; + ppd.visited = pointer_set_create (); + cp_walk_tree (&t, &find_parameter_packs_r, &ppd, ppd.visited); + pointer_set_destroy (ppd.visited); + + return parameter_packs; +} + /* Checks T for any "bare" parameter packs, which have not yet been expanded, and issues an error if any are found. This operation can only be done on full expressions or types (e.g., an expression @@ -3327,19 +3350,7 @@ make_pack_expansion (tree arg) bool check_for_bare_parameter_packs (tree t) { - tree parameter_packs = NULL_TREE; - struct find_parameter_pack_data ppd; - - if (!processing_template_decl || !t || t == error_mark_node) - return false; - - if (TREE_CODE (t) == TYPE_DECL) - t = TREE_TYPE (t); - - ppd.parameter_packs = ¶meter_packs; - ppd.visited = pointer_set_create (); - cp_walk_tree (&t, &find_parameter_packs_r, &ppd, ppd.visited); - pointer_set_destroy (ppd.visited); + tree parameter_packs = has_bare_parameter_packs (t); if (parameter_packs) { @@ -9065,6 +9076,86 @@ make_fnparm_pack (tree spec_parm) return extract_fnparm_pack (NULL_TREE, &spec_parm); } +/* Return true iff the Ith element of the argument pack ARG_PACK is + *NOT* a pack expansion. */ + +static bool +real_argument_pack_element_p (tree arg_pack, int i) +{ + return !PACK_EXPANSION_P (TREE_VEC_ELT + (ARGUMENT_PACK_ARGS (arg_pack), i)); +} + +/* Return true iff ARG_PACK is an argument pack that contains a pack + expansion. */ + +static bool +any_non_real_argument_pack_element_p (tree arg_pack) +{ + if (arg_pack == NULL_TREE + || arg_pack == error_mark_node + || !ARGUMENT_PACK_P (arg_pack)) + return false; + + for (int i = 0; i < TREE_VEC_LENGTH (ARGUMENT_PACK_ARGS (arg_pack)); ++i) + if (!real_argument_pack_element_p (arg_pack, i)) + return true; + return false; +} + +/* Creates an ARGUMENT_PACK_SELECT tree node, for the purpose of + substituting an argument pack into a pack expansion. This is a + subroutine of tsubst_pack_expansion. */ + +static tree +arg_pack_select_for_pack_expansion (tree arg_pack) +{ + tree aps = make_node (ARGUMENT_PACK_SELECT); + + if (!any_non_real_argument_pack_element_p (arg_pack)) + ARGUMENT_PACK_SELECT_FROM_PACK (aps) = arg_pack; + else + { + tree tmp_arg_pack = TYPE_P (arg_pack) + ? cxx_make_type (TREE_CODE (arg_pack)) + : make_node (TREE_CODE (arg_pack)); + tree tmp_vec = + make_tree_vec (TREE_VEC_LENGTH (ARGUMENT_PACK_ARGS (arg_pack))); + SET_ARGUMENT_PACK_ARGS (tmp_arg_pack, tmp_vec); + ARGUMENT_PACK_SELECT_FROM_PACK (aps) = tmp_arg_pack; + } + + return aps; +} + +/* Setup APS, which is an instance of an ARGUMENT_PACK_SELECT tree, so + that it selects the Ith argument out of the argument pack + ARG_PACK. If the Ith argument is a pack expansion, then just + select its pattern. Otherwise, select the whole argument. This + is a subroutine of tsubst_pack_expansion. */ + +static void +set_arg_pack_select_index_for_pack_expansion (tree aps, + int i, + tree arg_pack) +{ + if (any_non_real_argument_pack_element_p (arg_pack)) + { + tree args_vec = + ARGUMENT_PACK_ARGS (ARGUMENT_PACK_SELECT_FROM_PACK (aps)); + if (real_argument_pack_element_p (arg_pack, i)) + TREE_VEC_ELT (args_vec, i) = + TREE_VEC_ELT (ARGUMENT_PACK_ARGS (arg_pack), i); + else + TREE_VEC_ELT (args_vec, i) = + PACK_EXPANSION_PATTERN (TREE_VEC_ELT + (ARGUMENT_PACK_ARGS (arg_pack), + i)); + } + + ARGUMENT_PACK_SELECT_INDEX (aps) = i; +} + /* Substitute ARGS into T, which is an pack expansion (i.e. TYPE_PACK_EXPANSION or EXPR_PACK_EXPANSION). Returns a TREE_VEC with the substituted arguments, a PACK_EXPANSION_* node @@ -9284,20 +9375,21 @@ tsubst_pack_expansion (tree t, tree args, tsubst_flags_t complain, for (pack = packs; pack; pack = TREE_CHAIN (pack)) { tree parm = TREE_PURPOSE (pack); - tree arg; + tree arg_pack = TREE_VALUE (pack); + tree aps; /* instance of ARGUMENT_PACK_SELECT + tree. */ /* Select the Ith argument from the pack. */ if (TREE_CODE (parm) == PARM_DECL) { if (i == 0) { - arg = make_node (ARGUMENT_PACK_SELECT); - ARGUMENT_PACK_SELECT_FROM_PACK (arg) = TREE_VALUE (pack); + aps = arg_pack_select_for_pack_expansion (arg_pack); mark_used (parm); - register_local_specialization (arg, parm); + register_local_specialization (aps, parm); } else - arg = retrieve_local_specialization (parm); + aps = retrieve_local_specialization (parm); } else { @@ -9306,25 +9398,30 @@ tsubst_pack_expansion (tree t, tree args, tsubst_flags_t complain, if (i == 0) { - arg = make_node (ARGUMENT_PACK_SELECT); - ARGUMENT_PACK_SELECT_FROM_PACK (arg) = TREE_VALUE (pack); + aps = arg_pack_select_for_pack_expansion (arg_pack); /* Update the corresponding argument. */ - TMPL_ARG (args, level, idx) = arg; + TMPL_ARG (args, level, idx) = aps; } else /* Re-use the ARGUMENT_PACK_SELECT. */ - arg = TMPL_ARG (args, level, idx); + aps = TMPL_ARG (args, level, idx); } - ARGUMENT_PACK_SELECT_INDEX (arg) = i; + set_arg_pack_select_index_for_pack_expansion (aps, i, + arg_pack); } /* Substitute into the PATTERN with the altered arguments. */ if (!TYPE_P (pattern)) - TREE_VEC_ELT (result, i) = - tsubst_expr (pattern, args, complain, in_decl, - /*integral_constant_expression_p=*/false); + t = tsubst_expr (pattern, args, complain, in_decl, + /*integral_constant_expression_p=*/false); else - TREE_VEC_ELT (result, i) = tsubst (pattern, args, complain, in_decl); + t = tsubst (pattern, args, complain, in_decl); + + /* If the Ith argument pack element is a pack expansion, then + the Ith element resulting from the substituting is going to + be a pack expansion as well. */ + TREE_VEC_ELT (result, i) = + (has_bare_parameter_packs (t)) ? make_pack_expansion (t) : t; if (TREE_VEC_ELT (result, i) == error_mark_node) { diff --git a/gcc/testsuite/g++.dg/cpp0x/variadic139.C b/gcc/testsuite/g++.dg/cpp0x/variadic139.C new file mode 100644 index 0000000..89f7b32 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/variadic139.C @@ -0,0 +1,14 @@ +// Origin: PR c++/53609 +// { dg-do compile { target c++11 } } + +template<class...I> struct List {}; +template<int T> struct Z {static const int value = T;}; +template<int...T> using LZ = List<Z<T>...>; + +template<class...U> +struct F +{ + using N = LZ<U::value...>; +}; + +F<Z<1>, Z<2> >::N A; diff --git a/gcc/testsuite/g++.dg/cpp0x/variadic140.C b/gcc/testsuite/g++.dg/cpp0x/variadic140.C new file mode 100644 index 0000000..17ca9e5 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/variadic140.C @@ -0,0 +1,22 @@ +// Origin: PR c++/53609 +// { dg-do compile { target c++11 } } + +template<class...I> struct List{ static const bool is_ok = false;}; +template<int T> struct Z +{ + static const int value = T; + static const int value_square = T * T; +}; + +template<template<int> class U> +struct List<U<2>, U<3>, U<4>, U<9>> { static const bool is_ok = true;}; + +template<int...T> using LZ = List<Z<T>...>; + +template<class...T> +struct F +{ + using N = LZ<T::value..., T::value_square...>; +}; + +static_assert (F<Z<2>, Z<3>>::N::is_ok, ""); diff --git a/gcc/testsuite/g++.dg/cpp0x/variadic141.C b/gcc/testsuite/g++.dg/cpp0x/variadic141.C new file mode 100644 index 0000000..6b893a7 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/variadic141.C @@ -0,0 +1,22 @@ +// Origin: PR c++/53609 +// { dg-do compile { target c++11 } } + +template<class...I> struct List{ static const bool is_ok = false;}; +template<int T> struct Z +{ + static const int value = T; + static const int value_square = T * T; +}; + +template<template<int> class U> +struct List<U<2>, U<3>, U<4>, U<9>> { static const bool is_ok = true;}; + +template<int...T> using LZ = List<Z<T>...>; + +template<class...T> +struct F +{ + using N = LZ<T::value..., Z<4>::value, Z<9>::value>; +}; + +static_assert (F<Z<2>, Z<3>>::N::is_ok, ""); -- Dodji