aboutsummaryrefslogtreecommitdiff
path: root/gcc/cp/pt.c
diff options
context:
space:
mode:
authorJason Merrill <jason@redhat.com>2007-09-04 08:27:21 -0400
committerJason Merrill <jason@gcc.gnu.org>2007-09-04 08:27:21 -0400
commitdc28490d05f5ef25c5ff24ef946f48f402a20be8 (patch)
tree4ac72e6f1563682b5e0b76abbdf4a562498a349b /gcc/cp/pt.c
parenta1a826110720eda37c73f829daa4ee243ee953f5 (diff)
downloadgcc-dc28490d05f5ef25c5ff24ef946f48f402a20be8.zip
gcc-dc28490d05f5ef25c5ff24ef946f48f402a20be8.tar.gz
gcc-dc28490d05f5ef25c5ff24ef946f48f402a20be8.tar.bz2
re PR c++/14032 (Specialization of inner template using outer template argument doesn't work)
PR c++/14032 * pt.c (most_specialized_class): Substitute outer template arguments into the arguments of a member template partial specialization. (strip_innermost_template_args): New fn. From-SVN: r128076
Diffstat (limited to 'gcc/cp/pt.c')
-rw-r--r--gcc/cp/pt.c66
1 files changed, 65 insertions, 1 deletions
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 68716f1..aafb964 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -519,6 +519,37 @@ get_innermost_template_args (tree args, int n)
return new_args;
}
+/* The inverse of get_innermost_template_args: Return all but the innermost
+ EXTRA_LEVELS levels of template arguments from the ARGS. */
+
+static tree
+strip_innermost_template_args (tree args, int extra_levels)
+{
+ tree new_args;
+ int n = TMPL_ARGS_DEPTH (args) - extra_levels;
+ int i;
+
+ gcc_assert (n >= 0);
+
+ /* If N is 1, just return the outermost set of template arguments. */
+ if (n == 1)
+ return TMPL_ARGS_LEVEL (args, 1);
+
+ /* If we're not removing anything, just return the arguments we were
+ given. */
+ gcc_assert (extra_levels >= 0);
+ if (extra_levels == 0)
+ return args;
+
+ /* Make a new set of arguments, not containing the inner arguments. */
+ new_args = make_tree_vec (n);
+ for (i = 1; i <= n; ++i)
+ SET_TMPL_ARGS_LEVEL (new_args, i,
+ TMPL_ARGS_LEVEL (args, i));
+
+ return new_args;
+}
+
/* We've got a template header coming up; push to a new level for storing
the parms. */
@@ -13591,20 +13622,53 @@ most_specialized_class (tree type, tree tmpl)
int fate;
bool ambiguous_p;
tree args;
+ tree outer_args = NULL_TREE;
tmpl = most_general_template (tmpl);
args = CLASSTYPE_TI_ARGS (type);
+
+ /* For determining which partial specialization to use, only the
+ innermost args are interesting. */
+ if (TMPL_ARGS_HAVE_MULTIPLE_LEVELS (args))
+ {
+ outer_args = strip_innermost_template_args (args, 1);
+ args = INNERMOST_TEMPLATE_ARGS (args);
+ }
+
for (t = DECL_TEMPLATE_SPECIALIZATIONS (tmpl); t; t = TREE_CHAIN (t))
{
tree partial_spec_args;
tree spec_args;
+ tree parms = TREE_VALUE (t);
partial_spec_args = CLASSTYPE_TI_ARGS (TREE_TYPE (t));
- spec_args = get_class_bindings (TREE_VALUE (t),
+ if (outer_args)
+ {
+ int i;
+
+ /* Discard the outer levels of args, and then substitute in the
+ template args from the enclosing class. */
+ partial_spec_args = INNERMOST_TEMPLATE_ARGS (partial_spec_args);
+ partial_spec_args = tsubst_template_args
+ (partial_spec_args, outer_args, tf_none, NULL_TREE);
+
+ /* PARMS already refers to just the innermost parms, but the
+ template parms in partial_spec_args had their levels lowered
+ by tsubst, so we need to do the same for the parm list. We
+ can't just tsubst the TREE_VEC itself, as tsubst wants to
+ treat a TREE_VEC as an argument vector. */
+ parms = copy_node (parms);
+ for (i = TREE_VEC_LENGTH (parms) - 1; i >= 0; --i)
+ TREE_VEC_ELT (parms, i) =
+ tsubst (TREE_VEC_ELT (parms, i), outer_args, tf_none, NULL_TREE);
+ }
+ spec_args = get_class_bindings (parms,
partial_spec_args,
args);
if (spec_args)
{
+ if (outer_args)
+ spec_args = add_to_template_args (outer_args, spec_args);
list = tree_cons (spec_args, TREE_VALUE (t), list);
TREE_TYPE (list) = TREE_TYPE (t);
}