I am friendly pinging the patch below ...
Dodji Seketeli <do...@redhat.com> a écrit: > 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