Hello,

I have conflated the handling of two PRs here, because I think they
are related.

Consider this short example that illustrates the issue of PR
c++/51239:

    struct S {};

    template<typename T, typename...>
    using head = T;

    template<typename... Ts>
    using x = head<Ts...>;//#1

In #1, we want to be able to represent 'head<Ts...>', in such a way
that the Ts... argument is not substituted for the parameter of f the
"head" template.  Because the pack expansion Ts... means that we don't
yet have the proper arguments to "apply" to the head template.  Later
when we have those arguments, for instance:

    x<int, char> i0;

we can proceed with substituting the argument pack [int, char] into
the pack expansion Ts... to get a set of argument {int, char} that
we'll apply to the head template, to get "int".

To date we don't have such an "unbound alias template specialization"
representation, because we leverage on the fact that "head" is an
alias template to substitute its arguments into its underlying type
directly.  Doing that in the present case (the argument being a pack
expansion) just wreaks havoc as the PR can attest.

After talking with you offline, we settled on using the existing
BOUND_TEMPLATE_TEMPLATE_PARM tree to represent this new construct.
The alias template and its unbound arguments are stored in the
TYPE_TEMPLATE_INFO of the tree and its TYPE_NAME has the
TYPE_DECL_ALIAS_P flag set.

So now, during the process of building a template specialization,
coerce_template_parms detects that we don't have all the arguments yet
- that is, when one of the arguments is a pack expansion.  In that
case lookup_template_class_1, if the template is an alias template,
builds an unbound alias template specialization and returns it.

We also support substituting for an unbound alias template
specialization.

The problem in PR c++/51180 is that sometimes coerce_template_parms
won't let us build a type "foo<T...>" if the template foo has more
than one parameter and no parameter pack.  We then end up in the "if"
below, and error out:

  if ((nargs > nparms && !variadic_p)
      || (nargs < nparms - variadic_p     
          && require_all_args
          && (!use_default_args
              || (TREE_VEC_ELT (parms, nargs) != error_mark_node
                  && !TREE_PURPOSE (TREE_VEC_ELT (parms, nargs))))))
    {

So the patch below tries to fix that as well.

Incidentally, I noticed that the test g++.dg/cpp0x/alias-decl-15.C
should actually be considered valid.  I wrongly thought otherwise at
that time.  I have thus adjusted that test case accordingly.

Bootstrapped and tested on x86_64-unknown-linux-gnu against trunk.

gcc/cp/

        PR c++/51239
        PR c++/51180
        * cp-tree.h (UNBOUND_ALIAS_TEMPLATE_P): New
        predicate.
        * pt.c (build_unbound_alias_template): New.
        (coerce_template_parms): Take a new out parameter flag about if
        the actual number of arguments is unknown.  Make the template
        unbound if one of its arguments is a pack expansion.
        (lookup_template_class_1): Adjust for new argument to
        coerce_template_parms.  Build an unbound alias template if the
        number of arguments is not known yet.
        (tsubst)<BOUND_TEMPLATE_TEMPLATE_PARM>: Handle unbound alias
        templates.
        (fn_type_unification, get_bindings, most_specialized_class):
        Adjust for new argument to coerce_template_parms.

gcc/testsuite/

        PR c++/51239
        PR c++/51180
        * g++.dg/cpp0x/alias-decl-18.C: New test.
        * g++.dg/cpp0x/alias-decl-19.C: Likewise.
        * g++.dg/cpp0x/alias-decl-15.C: This was wrongly expected
        to fail before.  Adjust accordingly.
---
 gcc/cp/cp-tree.h                           |   10 +++
 gcc/cp/pt.c                                |  108 +++++++++++++++++++++++++---
 gcc/testsuite/g++.dg/cpp0x/alias-decl-15.C |   10 ++--
 gcc/testsuite/g++.dg/cpp0x/alias-decl-18.C |   29 ++++++++
 gcc/testsuite/g++.dg/cpp0x/alias-decl-19.C |   19 +++++
 5 files changed, 161 insertions(+), 15 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp0x/alias-decl-18.C
 create mode 100644 gcc/testsuite/g++.dg/cpp0x/alias-decl-19.C

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 3f4f408..0dcebd6 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -3662,6 +3662,16 @@ more_aggr_init_expr_args_p (const 
aggr_init_expr_arg_iterator *iter)
   (DECL_TYPE_TEMPLATE_P (NODE)                         \
    && !DECL_ARTIFICIAL (DECL_TEMPLATE_RESULT (NODE)))
 
+/* Nonzero for a node representing an alias template specialization in
+   which the arguments are not yet applied to the alias template.
+   This is used in cases where the arguments are not all fully known
+   yet, and can be applied later when they are.  */
+#define UNBOUND_ALIAS_TEMPLATE_P(NODE)                         \
+  ((NODE)                                                      \
+   && TREE_CODE (NODE) == BOUND_TEMPLATE_TEMPLATE_PARM         \
+   && DECL_ALIAS_TEMPLATE_P (TYPE_TI_TEMPLATE (NODE))          \
+   && TYPE_DECL_ALIAS_P (TYPE_NAME (NODE)))
+
 /* Nonzero for a NODE which declares a type.  */
 #define DECL_DECLARES_TYPE_P(NODE) \
   (TREE_CODE (NODE) == TYPE_DECL || DECL_CLASS_TEMPLATE_P (NODE))
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 2ba26b2..b0b6016 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -130,7 +130,7 @@ static tree reopen_tinst_level (struct tinst_level *);
 static tree tsubst_initializer_list (tree, tree);
 static tree get_class_bindings (tree, tree, tree);
 static tree coerce_template_parms (tree, tree, tree, tsubst_flags_t,
-                                  bool, bool);
+                                  bool, bool, bool*);
 static void tsubst_enum        (tree, tree, tree);
 static tree add_to_template_args (tree, tree);
 static tree add_outermost_template_args (tree, tree);
@@ -6711,7 +6711,8 @@ coerce_template_parms (tree parms,
                       tree in_decl,
                       tsubst_flags_t complain,
                       bool require_all_args,
-                      bool use_default_args)
+                      bool use_default_args,
+                      bool *unknown_nb_args_p_ptr)
 {
   int nparms, nargs, parm_idx, arg_idx, lost = 0;
   tree inner_args;
@@ -6719,6 +6720,7 @@ coerce_template_parms (tree parms,
   tree new_inner_args;
   int saved_unevaluated_operand;
   int saved_inhibit_evaluation_warnings;
+  bool unknown_nb_args_p = false;
 
   /* When used as a boolean value, indicates whether this is a
      variadic template parameter list. Since it's an int, we can also
@@ -6755,9 +6757,16 @@ coerce_template_parms (tree parms,
     inner_args = expand_template_argument_pack (inner_args);
 
   nargs = inner_args ? NUM_TMPL_ARGS (inner_args) : 0;
+  for (arg_idx = 0; arg_idx < nargs && !unknown_nb_args_p; ++arg_idx)
+    {
+      tree elt = TREE_VEC_ELT (inner_args, arg_idx);
+      unknown_nb_args_p = elt && PACK_EXPANSION_P (elt);
+    }
+
   if ((nargs > nparms && !variadic_p)
-      || (nargs < nparms - variadic_p
+      || (nargs < nparms - variadic_p    
          && require_all_args
+         && !unknown_nb_args_p
          && (!use_default_args
              || (TREE_VEC_ELT (parms, nargs) != error_mark_node
                   && !TREE_PURPOSE (TREE_VEC_ELT (parms, nargs))))))
@@ -6877,6 +6886,9 @@ coerce_template_parms (tree parms,
   cp_unevaluated_operand = saved_unevaluated_operand;
   c_inhibit_evaluation_warnings = saved_inhibit_evaluation_warnings;
 
+  if (unknown_nb_args_p_ptr)
+   *unknown_nb_args_p_ptr = unknown_nb_args_p;
+
   if (lost)
     return error_mark_node;
 
@@ -7111,6 +7123,38 @@ maybe_get_template_decl_from_type_decl (tree decl)
     ? CLASSTYPE_TI_TEMPLATE (TREE_TYPE (decl)) : decl;
 }
 
+/* Build an unsubstituted alias template specialization.
+
+   This is an alias template specialization in which the arguments are
+   not yet applied to the alias template.  This construct is
+   represented by a BOUND_TEMPLATE_TEMPLATE_PARM tree and makes the
+   predicate UNBOUND_ALIAS_TEMPLATE_P be true.  */
+
+static tree
+build_unbound_alias_template (tree tmpl,
+                             tree arglist,
+                             source_location location)
+{
+  tree decl, result;
+
+  gcc_assert (TREE_CODE (tmpl) == TEMPLATE_DECL
+             && DECL_ALIAS_TEMPLATE_P (tmpl));
+
+  result = cxx_make_type (BOUND_TEMPLATE_TEMPLATE_PARM);
+  decl = build_decl (location, TYPE_DECL,
+                    DECL_NAME (tmpl), NULL_TREE);
+  TYPE_DECL_ALIAS_P (decl) = 1;
+  TREE_TYPE (decl) = result;
+  TYPE_NAME (result) = decl;
+  TYPE_SIZE (result) = 0;
+  SET_TYPE_STRUCTURAL_EQUALITY (result);
+  TEMPLATE_TEMPLATE_PARM_TEMPLATE_INFO (result) =
+    build_template_info (tmpl, arglist);
+
+  return result;
+  
+}
+
 /* Given an IDENTIFIER_NODE (type TEMPLATE_DECL) and a chain of
    parameters, find the desired type.
 
@@ -7259,7 +7303,8 @@ lookup_template_class_1 (tree d1, tree arglist, tree 
in_decl, tree context,
       arglist2 = coerce_template_parms (parmlist, arglist, templ,
                                        complain,
                                        /*require_all_args=*/true,
-                                       /*use_default_args=*/true);
+                                       /*use_default_args=*/true,
+                                       NULL);
       if (arglist2 == error_mark_node
          || (!uses_template_parms (arglist2)
              && check_instantiated_args (templ, arglist2, complain)))
@@ -7278,6 +7323,7 @@ lookup_template_class_1 (tree d1, tree arglist, tree 
in_decl, tree context,
       int parm_depth;
       int is_dependent_type;
       int use_partial_inst_tmpl = false;
+      bool unknown_nb_args_p = false;
 
       if (template_type == error_mark_node)
        /* An error occured while building the template TEMPL, and a
@@ -7338,7 +7384,8 @@ lookup_template_class_1 (tree d1, tree arglist, tree 
in_decl, tree context,
                                           arglist, gen_tmpl,
                                           complain,
                                           /*require_all_args=*/true,
-                                          /*use_default_args=*/true);
+                                          /*use_default_args=*/true,
+                                          &unknown_nb_args_p);
              else
                /* Outer levels should have already been coerced.  */
                a = TMPL_ARGS_LEVEL (arglist, i);
@@ -7372,7 +7419,8 @@ lookup_template_class_1 (tree d1, tree arglist, tree 
in_decl, tree context,
                                   gen_tmpl,
                                   complain,
                                   /*require_all_args=*/true,
-                                  /*use_default_args=*/true);
+                                  /*use_default_args=*/true,
+                                  &unknown_nb_args_p);
 
       if (arglist == error_mark_node)
        /* We were unable to bind the arguments.  */
@@ -7456,6 +7504,18 @@ lookup_template_class_1 (tree d1, tree arglist, tree 
in_decl, tree context,
        }
       else if (DECL_ALIAS_TEMPLATE_P (gen_tmpl))
        {
+         if (unknown_nb_args_p)
+           {
+             /* So we don't yet know the number of arguments the
+                alias template specialization has.  Let's build a
+                representation that keeps the arguments "unapplied"
+                to the alias template for now, so that we can apply
+                the them later when they are fully known.  */
+             t = build_unbound_alias_template (gen_tmpl,
+                                               arglist,
+                                               input_location);
+             return t;
+           }
          /* The user referred to a specialization of an alias
            template represented by GEN_TMPL.
 
@@ -11200,6 +11260,30 @@ tsubst (tree t, tree args, tsubst_flags_t complain, 
tree in_decl)
        r = NULL_TREE;
 
        gcc_assert (TREE_VEC_LENGTH (args) > 0);
+
+       if (UNBOUND_ALIAS_TEMPLATE_P (t))
+         {
+           /* T represents an alias template specialization in which
+              the number of arguments are not yet fully known.  They
+              contain e.g, a pack expansion.  At this point we
+              probably can deduce the full set of arguments to apply
+              to the alias template.  */
+
+           /* So let's deduce those arguments ...  */
+           tree argvec = tsubst (TYPE_TI_ARGS (t),
+                                 args, complain, in_decl);
+           if (argvec == error_mark_node)
+             return error_mark_node;
+
+           /* ... and apply them to the alias template.  */
+           r = lookup_template_class (TYPE_TI_TEMPLATE (t),
+                                      argvec, in_decl,
+                                      /*con text=*/NULL_TREE,
+                                      /*entering_scope=*/0,
+                                      complain);
+           return r;
+         }
+
        template_parm_level_and_index (t, &level, &idx); 
 
        levels = TMPL_ARGS_DEPTH (args);
@@ -14719,7 +14803,8 @@ fn_type_unification (tree fn,
                                   ? tf_warning_or_error
                                   : tf_none),
                                   /*require_all_args=*/false,
-                                  /*use_default_args=*/false));
+                                 /*use_default_args=*/false,
+                                 NULL));
       if (converted_args == error_mark_node)
        return 1;
 
@@ -16256,7 +16341,8 @@ unify (tree tparms, tree targs, tree parm, tree arg, 
int strict,
                                        ? tf_warning_or_error
                                        : tf_none),
                                       /*require_all_args=*/true,
-                                      /*use_default_args=*/false)
+                                      /*use_default_args=*/false,
+                                      NULL)
                == error_mark_node)
              return 1;
 
@@ -17334,7 +17420,8 @@ get_bindings (tree fn, tree decl, tree explicit_args, 
bool check_rettype)
                                 explicit_args, NULL_TREE,
                                 tf_none,
                                 /*require_all_args=*/false,
-                                /*use_default_args=*/false);
+                                /*use_default_args=*/false,
+                                NULL);
       if (converted_args == error_mark_node)
        return NULL_TREE;
 
@@ -17637,7 +17724,8 @@ most_specialized_class (tree type, tree tmpl, 
tsubst_flags_t complain)
                                                       partial_spec_args),
                                 tmpl, tf_none,
                                 /*require_all_args=*/true,
-                                /*use_default_args=*/true);
+                                /*use_default_args=*/true,
+                                NULL);
 
       --processing_template_decl;
 
diff --git a/gcc/testsuite/g++.dg/cpp0x/alias-decl-15.C 
b/gcc/testsuite/g++.dg/cpp0x/alias-decl-15.C
index 2bc9b11..e4b1d46 100644
--- a/gcc/testsuite/g++.dg/cpp0x/alias-decl-15.C
+++ b/gcc/testsuite/g++.dg/cpp0x/alias-decl-15.C
@@ -1,8 +1,8 @@
 // Origin PR c++/51194
 // { dg-options "-std=c++0x" }
 
-template<class U, class V> //#1
-struct foo {}; // { dg-error "provided for|foo" }
+template<class U, class V>
+struct foo {};
 
 template<class U, class V=char>
 struct P {};
@@ -10,8 +10,8 @@ struct P {};
 template<template<class... U> class... TT>
 struct bar {
     template<class... Args>
-    using mem = P<TT<Args...>...>;//#2 { dg-error "wrong number of|arguments" }
+    using mem = P<TT<Args...>...>;
 };
 
-bar<foo>::mem<int, char> b;//#3 { dg-error "invalid type" }
-
+bar<foo> f;
+bar<foo>::mem<int, char> b;
diff --git a/gcc/testsuite/g++.dg/cpp0x/alias-decl-18.C 
b/gcc/testsuite/g++.dg/cpp0x/alias-decl-18.C
new file mode 100644
index 0000000..311ab58
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/alias-decl-18.C
@@ -0,0 +1,29 @@
+// Origin PR c++/51239
+// { dg-options "-std=c++11" }
+
+struct S {};
+
+template<typename T, typename...>
+using head = T;
+
+template<typename... Ts>
+using x = head<Ts...>;
+
+x<int, char> i0;
+x<char> i1;
+x<unsigned, int, int> i2;
+x<S, char>i3;
+
+void f0(int);
+void f1(char);
+void f2(unsigned);
+void f3(S);
+
+void
+g()
+{
+    f0(i0);
+    f1(i1);
+    f2(i2);
+    f3(i3);
+}
diff --git a/gcc/testsuite/g++.dg/cpp0x/alias-decl-19.C 
b/gcc/testsuite/g++.dg/cpp0x/alias-decl-19.C
new file mode 100644
index 0000000..ff7b822
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/alias-decl-19.C
@@ -0,0 +1,19 @@
+// Origin PR c++/51180
+// { dg-options "-std=c++11" }
+
+template<class, class>
+struct t2 // { dg-error "provided for" }
+{
+};
+
+template<template<class...> class M>
+struct m
+{
+    template<class... B>
+    using inner = M<B...>; // { dg-error "wrong number of template arg" }
+};
+
+m<t2> sta1; // <-- this one is valid
+m<t2>::inner<int, int> sta2; // <-- and this one as well
+m<t2>::inner<char> sta3;// { dg-error "invalid" }
+m<t2>::inner<char, int, int> sta4;// { dg-error "invalid" }
-- 
1.7.6.4


-- 
                Dodji

Reply via email to