This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
Re: PING: C++0x variadic templates fixes
- From: "Doug Gregor" <doug dot gregor at gmail dot com>
- To: "Jason Merrill" <jason at redhat dot com>
- Cc: "GCC Patches" <gcc-patches at gcc dot gnu dot org>
- Date: Fri, 26 Oct 2007 13:11:30 -0400
- Subject: Re: PING: C++0x variadic templates fixes
- References: <24b520d20707251230r2de5b868l85159e833b1490e9@mail.gmail.com> <46C5F718.3060304@redhat.com>
On 8/17/07, Jason Merrill <jason@redhat.com> wrote:
> > http://gcc.gnu.org/ml/gcc-patches/2007-07/msg00365.html
>
> > (tsubst_pack_expansion): We our substitution of a parameter pack
> > is a "trivial" substitution of itself, just substitute into the
>
> We?
Heh, "When" was intended there...
> Your changes to convert_template_argument cause check_arg to be used
> much more. Do we still need the distinction between arg and check_arg?
We need to keep the original argument around in some cases, for error
messages and to safely back out of some error conditions. I've taken
another sweep through this routine: we now work on just "arg", which
plays the same role as "check_arg" did before. We keep around
"orig_arg" for those cases where we really need the original argument
(before we saw through the TYPE_PACK_EXPANSION).
> > + if ((TYPE_P (pattern) && same_type_p (pattern, parm_pack))
> > + || (!TYPE_P (pattern) && cp_tree_equal (parm_pack, pattern)))
> > + /* 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. */
It happens in the test for PR 31993, here:
template<typename...> struct A;
template<template<int> class... T> struct A<T<0>...>
{
template<int> struct B {};
B<0> b;
};
When we lookup 'B<0>' (in lookup_template_class), we find
A<T<0>...>::B<0>. In performing the substitution for the template
arguments of A<T<0>...>::B, we substitute both the outer level of
arguments and the inner level of arguments. In the outer level of
arguments, we end up substituting the expansion of 'T' for 'T'.
Normally, substituting into an expansion produces a new argument pack,
but not here: we want to retain the pack expansion because nothing is
actually being expanded.
> > + /* We can get a pack expansion here when we are
> > + instantiating a member template and ARG is a pack
> > + expansion from the outer template. */
> > + if (PACK_EXPANSION_P (arg))
> > + arg = PACK_EXPANSION_PATTERN (arg);
>
> Can you give me an example of how these two situations come up? It's
> not clear to me how the testcases relate to the fixes.
Ah, this case no longer happens here, because we've dealt with it in
tsubst_pack_expansion above. I've removed this cruft.
Here's an updated patch. Tested i686-pc-linux-gnu, no regressions. It
fixes the two PRs mentioned in the original e-mail (32252 and 31993).
- Doug
2007-10-26 Douglas Gregor <doug.gregor@gmail.com>
PR c++/31993
PR c++/32252
* pt.c (find_parameter_packs_r): Fix typo in comment.
(convert_template_argument): Look at the pattern of a pack
expansion to determine what kind of entity we're converting.
(coerce_template_parameter_pack): When we have coerced a non-type
template parameter pack, substitute into the type of that pack.
(tsubst_pack_expansion): When our substitution of a parameter pack
is a "trivial" substitution of itself, just substitute into the
pack expansion rather than actually expanding.
2007-10-26 Douglas Gregor <doug.gregor@gmail.com>
* g++.dg/cpp0x/pr31993.C: New
* g++.dg/cpp0x/pr32252.C: New
Index: testsuite/g++.dg/cpp0x/pr31993.C
===================================================================
--- testsuite/g++.dg/cpp0x/pr31993.C (revision 0)
+++ testsuite/g++.dg/cpp0x/pr31993.C (revision 0)
@@ -0,0 +1,9 @@
+// { dg-options "-std=c++0x" }
+
+template<typename...> struct A;
+
+template<template<int> class... T> struct A<T<0>...>
+{
+ template<int> struct B {};
+ B<0> b;
+};
Index: testsuite/g++.dg/cpp0x/pr32252.C
===================================================================
--- testsuite/g++.dg/cpp0x/pr32252.C (revision 0)
+++ testsuite/g++.dg/cpp0x/pr32252.C (revision 0)
@@ -0,0 +1,8 @@
+// { dg-options "-std=c++0x" }
+int x[5];
+
+template<int M, int N, int (&... p)[N]> struct A;
+
+template<int M> struct A<M,5,x> {};
+
+A<0,5,x> a;
Index: cp/pt.c
===================================================================
--- cp/pt.c (revision 129655)
+++ cp/pt.c (working copy)
@@ -2416,9 +2416,9 @@ struct find_parameter_pack_data
struct pointer_set_t *visited;
};
-/* Identifiers all of the argument packs that occur in a template
+/* Identifies all of the argument packs that occur in a template
argument and appends them to the TREE_LIST inside DATA, which is a
- find_parameter_pack_Data structure. This is a subroutine of
+ find_parameter_pack_data structure. This is a subroutine of
make_pack_expansion and uses_parameter_packs. */
static tree
find_parameter_packs_r (tree *tp, int *walk_subtrees, void* data)
@@ -4657,9 +4657,9 @@ convert_template_argument (tree parm,
int i,
tree in_decl)
{
+ tree orig_arg;
tree val;
int is_type, requires_type, is_tmpl_type, requires_tmpl_type;
- tree check_arg = arg;
if (TREE_CODE (arg) == TREE_LIST
&& TREE_CODE (TREE_VALUE (arg)) == OFFSET_REF)
@@ -4669,24 +4669,26 @@ convert_template_argument (tree parm,
invalid, but static members are OK. In any
case, grab the underlying fields/functions
and issue an error later if required. */
- arg = TREE_VALUE (arg);
+ orig_arg = TREE_VALUE (arg);
TREE_TYPE (arg) = unknown_type_node;
}
+ orig_arg = arg;
+
requires_tmpl_type = TREE_CODE (parm) == TEMPLATE_DECL;
requires_type = (TREE_CODE (parm) == TYPE_DECL
|| requires_tmpl_type);
/* When determining whether an argument pack expansion is a template,
look at the pattern. */
- if (TREE_CODE (check_arg) == TYPE_PACK_EXPANSION)
- check_arg = PACK_EXPANSION_PATTERN (check_arg);
+ if (TREE_CODE (arg) == TYPE_PACK_EXPANSION)
+ arg = PACK_EXPANSION_PATTERN (arg);
is_tmpl_type =
- ((TREE_CODE (check_arg) == TEMPLATE_DECL
- && TREE_CODE (DECL_TEMPLATE_RESULT (check_arg)) == TYPE_DECL)
- || TREE_CODE (check_arg) == TEMPLATE_TEMPLATE_PARM
- || TREE_CODE (check_arg) == UNBOUND_CLASS_TEMPLATE);
+ ((TREE_CODE (arg) == TEMPLATE_DECL
+ && TREE_CODE (DECL_TEMPLATE_RESULT (arg)) == TYPE_DECL)
+ || TREE_CODE (arg) == TEMPLATE_TEMPLATE_PARM
+ || TREE_CODE (arg) == UNBOUND_CLASS_TEMPLATE);
if (is_tmpl_type
&& (TREE_CODE (arg) == TEMPLATE_TEMPLATE_PARM
@@ -4699,12 +4701,13 @@ convert_template_argument (tree parm,
&& TREE_CODE (TREE_OPERAND (arg, 0)) == TEMPLATE_TYPE_PARM)
{
pedwarn ("to refer to a type member of a template parameter, "
- "use %<typename %E%>", arg);
+ "use %<typename %E%>", orig_arg);
- arg = make_typename_type (TREE_OPERAND (arg, 0),
- TREE_OPERAND (arg, 1),
- typename_type,
- complain & tf_error);
+ orig_arg = make_typename_type (TREE_OPERAND (arg, 0),
+ TREE_OPERAND (arg, 1),
+ typename_type,
+ complain & tf_error);
+ arg = orig_arg;
is_type = 1;
}
if (is_type != requires_type)
@@ -4719,11 +4722,11 @@ convert_template_argument (tree parm,
if (is_type)
error (" expected a constant of type %qT, got %qT",
TREE_TYPE (parm),
- (is_tmpl_type ? DECL_NAME (arg) : arg));
+ (is_tmpl_type ? DECL_NAME (arg) : orig_arg));
else if (requires_tmpl_type)
- error (" expected a class template, got %qE", arg);
+ error (" expected a class template, got %qE", orig_arg);
else
- error (" expected a type, got %qE", arg);
+ error (" expected a type, got %qE", orig_arg);
}
}
return error_mark_node;
@@ -4738,7 +4741,7 @@ convert_template_argument (tree parm,
if (is_tmpl_type)
error (" expected a type, got %qT", DECL_NAME (arg));
else
- error (" expected a class template, got %qT", arg);
+ error (" expected a class template, got %qT", orig_arg);
}
return error_mark_node;
}
@@ -4756,19 +4759,13 @@ convert_template_argument (tree parm,
tree parmparm = DECL_INNERMOST_TEMPLATE_PARMS (parm);
tree argparm;
- check_arg = arg;
- /* When determining whether a pack expansion is a template,
- look at the pattern. */
- if (TREE_CODE (check_arg) == TYPE_PACK_EXPANSION)
- check_arg = PACK_EXPANSION_PATTERN (check_arg);
-
- argparm = DECL_INNERMOST_TEMPLATE_PARMS (check_arg);
+ argparm = DECL_INNERMOST_TEMPLATE_PARMS (arg);
if (coerce_template_template_parms (parmparm, argparm,
complain, in_decl,
args))
{
- val = arg;
+ val = orig_arg;
/* TEMPLATE_TEMPLATE_PARM node is preferred over
TEMPLATE_DECL. */
@@ -4777,9 +4774,9 @@ convert_template_argument (tree parm,
if (DECL_TEMPLATE_TEMPLATE_PARM_P (val))
val = TREE_TYPE (val);
else if (TREE_CODE (val) == TYPE_PACK_EXPANSION
- && DECL_TEMPLATE_TEMPLATE_PARM_P (check_arg))
+ && DECL_TEMPLATE_TEMPLATE_PARM_P (arg))
{
- val = TREE_TYPE (check_arg);
+ val = TREE_TYPE (arg);
val = make_pack_expansion (val);
}
}
@@ -4792,7 +4789,7 @@ convert_template_argument (tree parm,
"template parameter list for %qD",
i + 1, in_decl);
error (" expected a template of type %qD, got %qD",
- parm, arg);
+ parm, orig_arg);
}
val = error_mark_node;
@@ -4800,7 +4797,7 @@ convert_template_argument (tree parm,
}
}
else
- val = arg;
+ val = orig_arg;
/* We only form one instance of each template specialization.
Therefore, if we use a non-canonical variant (i.e., a
typedef), any future messages referring to the type will use
@@ -4816,7 +4813,7 @@ convert_template_argument (tree parm,
if (invalid_nontype_parm_type_p (t, complain))
return error_mark_node;
- if (!uses_template_parms (arg) && !uses_template_parms (t))
+ if (!uses_template_parms (orig_arg) && !uses_template_parms (t))
/* We used to call digest_init here. However, digest_init
will report errors, which we don't want when complain
is zero. More importantly, digest_init will try too
@@ -4827,14 +4824,14 @@ convert_template_argument (tree parm,
conversions can occur is part of determining which
function template to call, or whether a given explicit
argument specification is valid. */
- val = convert_nontype_argument (t, arg);
+ val = convert_nontype_argument (t, orig_arg);
else
- val = arg;
+ val = orig_arg;
if (val == NULL_TREE)
val = error_mark_node;
else if (val == error_mark_node && (complain & tf_error))
- error ("could not convert template argument %qE to %qT", arg, t);
+ error ("could not convert template argument %qE to %qT", orig_arg, t);
}
return val;
@@ -4948,7 +4945,8 @@ coerce_template_parameter_pack (tree par
else
{
argument_pack = make_node (NONTYPE_ARGUMENT_PACK);
- TREE_TYPE (argument_pack) = TREE_TYPE (TREE_VALUE (parm));
+ TREE_TYPE (argument_pack)
+ = tsubst (TREE_TYPE (TREE_VALUE (parm)), args, complain, in_decl);
TREE_CONSTANT (argument_pack) = 1;
}
@@ -7097,6 +7095,22 @@ tsubst_pack_expansion (tree t, tree args
return result;
}
+ 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);
+ if ((TYPE_P (pattern) && same_type_p (pattern, parm_pack))
+ || (!TYPE_P (pattern) && cp_tree_equal (parm_pack, pattern)))
+ /* 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)
{
int my_len =