Patchwork PR c++/47311

login
register
mail settings
Submitter Dodji Seketeli
Date Jan. 20, 2011, 3:40 p.m.
Message ID <m3hbd3lh2c.fsf@redhat.com>
Download mbox | patch
Permalink /patch/79708/
State New
Headers show

Comments

Dodji Seketeli - Jan. 20, 2011, 3:40 p.m.
Hello,

In this example:

 template<typename> class A;
 template<class Key,
	  class T,
	  template<typename TF = T> class TC = A> class B;

tsubst crashes during the fixup of the default argument T of parameter
TF because it is called with a wrong argument list.

The first time G++ sees template<typename TF = T> class TC, the
parameters of B haven't been processed "enough" for them to appear on
current_template_parms; that is, end_template_parm_list hasn't been
called on them yet. So during the fixup of TF (and its default
argument) as part of the parsing of 'template<typename TF = T> class TC',
current_template_arg can't return any argument for
{Key, T, TC}. Rather it returns {NULL, NULL, NULL}.

The full argument list that is substituted into the default template
argument T is then [{NULL, NULL, NULL} {TF}] while I would have
naively expected it to be [{Key, T, TC} {TF}] .The index of T inside
the parameters of B is 1 so it matches a NULL argument in the argument
list. Hence the crash.

I think we are trying to fixup the default argument of TF too
early. We should wait until the point where all the parameters of B
are processed to attempt a fixup of all the default arguments and
non-type parameters at once.

Besides, we should fixup the parameters of a given template template
parameter P before fixing up the type of P itself because the
parameters are needed to computed a proper canonical type for P -- I
was doing the opposite.

The patch below addresses these two points and has been tested on
x86_64-unknown-linux-gnu against trunk.
Jason Merrill - Jan. 21, 2011, 10:14 p.m.
On 01/20/2011 10:40 AM, Dodji Seketeli wrote:
> +      if (incomplete_args_for_template_parm_fixup_p (arglist, parm))

It seems to me that if we do things in the right order, we shouldn't 
need this test.  Can't we arrange to defer fixing up template template 
parameter template parameters some other way than testing what's in the 
arglist?

Jason

Patch

diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 01f8cd7..0246948 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -192,6 +192,7 @@  static tree listify (tree);
 static tree listify_autos (tree, tree);
 static tree template_parm_to_arg (tree t);
 static tree current_template_args (void);
+static bool incomplete_args_for_template_parm_fixup_p (tree, tree);
 static tree fixup_template_type_parm_type (tree, int);
 static tree fixup_template_parm_index (tree, tree, int);
 static void fixup_template_parms (void);
@@ -3774,21 +3775,16 @@  fixup_template_parm (tree parm_desc,
     {
       /* PARM is a template template parameter. This is going to
 	 be interesting.  */
-      tree tparms, targs, innermost_args;
+      tree tparms, targs, innermost_args, t;
       int j;
 
-      /* First, fix up the type of the parm.  */
+      /* First, fix up the parms of the template template parm
+	 because the parms are involved in defining the new canonical
+	 type of the template template parm.  */
 
-      tree t =
-	fixup_template_type_parm_type (TREE_TYPE (parm), num_parms);
-      TREE_TYPE (parm) = t;
-
-      TREE_VEC_ELT (fixedup_args, idx) =
-	template_parm_to_arg (parm_desc);
-
-      /* Now we need to substitute the template parm types that
-	 have been fixed up so far into the non-type template
-	 parms of this template template parm. E.g, consider this:
+      /* So we need to substitute the template parm types that have
+	 been fixed up so far into the template parms of this template
+	 template parm. E.g, consider this:
 
 	 template<class T, template<T u> class TT> class S;
 
@@ -3827,9 +3823,40 @@  fixup_template_parm (tree parm_desc,
 			       TREE_VEC_LENGTH (tparms),
 			       targs);
 	}
+
+      /* Now fix up the type of the template template parm.  */
+
+      t = fixup_template_type_parm_type (TREE_TYPE (parm), num_parms);
+      TREE_TYPE (parm) = t;
+
+      TREE_VEC_ELT (fixedup_args, idx) =
+	template_parm_to_arg (parm_desc);
     }
   else if (TREE_CODE (parm) == PARM_DECL)
     {
+      tree index, pushed_decl, fixed_up_index;
+
+      if (incomplete_args_for_template_parm_fixup_p (arglist, parm))
+	/* If arglist is incomplete for fixing up PARM, that might mean
+	   PARM is a non-type parameter of a template template
+	   parameter that we are attempting to fixup too early. E.g:
+
+	    template <int> class A;
+	    template <class Key, class T, template <T p> class TC = A> class B;
+
+	   The first time we see the parm p, {Key, T, TC} have
+	   obviously not been added to the current_template_parameters
+	   yet. So the implicit ARGLIST returned by
+	   current_template_args to substitute into p is
+	   [{NULL,NULL,NULL}, {NULL}] instead of [{Key, T, NULL},
+	   {NULL}].
+	   
+	   We cannot use that incomplete ARGLIST then. So let's bail
+	   out for now, and when and Key and T have been fixed up, we
+	   will be called again to fixup p and that time with the
+	   complete set of implicit arguments.  */
+	goto end;
+
       /* PARM is a non-type template parameter. We need to:
 
        * Fix up its TEMPLATE_PARM_INDEX to make it carry the
@@ -3843,7 +3870,7 @@  fixup_template_parm (tree parm_desc,
 
        * into the type of PARM.  */
 
-      tree index = DECL_INITIAL (parm);
+      index = DECL_INITIAL (parm);
 
       /* PUSHED_DECL is the decl added to the symbol table with
 	 the name of the parameter. E,g:
@@ -3856,12 +3883,12 @@  fixup_template_parm (tree parm_desc,
 	 PUSHED_DECL is. We need to replace the reference to the
 	 old TEMPLATE_PARM_INDEX carried by PUSHED_DECL by the
 	 fixed-up TEMPLATE_PARM_INDEX.  */
-      tree pushed_decl = TEMPLATE_PARM_DECL (index);
-
+      pushed_decl = TEMPLATE_PARM_DECL (index);
+      
       /* Let's fix up the TEMPLATE_PARM_INDEX then. Note that we must
 	 fixup the type of PUSHED_DECL as well and luckily
 	 fixup_template_parm_index does it for us too.  */
-      tree fixed_up_index =
+      fixed_up_index =
 	fixup_template_parm_index (index, arglist, num_parms);
 
       DECL_INITIAL (pushed_decl) = DECL_INITIAL (parm) = fixed_up_index;
@@ -3875,14 +3902,84 @@  fixup_template_parm (tree parm_desc,
 				 tf_none, NULL_TREE);
     }
 
-  TREE_PURPOSE (parm_desc) =
-    tsubst_template_arg (TREE_PURPOSE (parm_desc),
-			 arglist, tf_none, parm);
-
+  /* Don't try to substitute the implicit arguments ARGLIST into the
+     default parameter of a template template parameter too early,
+     e.g:
+     
+      template < typename > class A;
+      template <class Key,
+                class T,
+		template<typename TF = T> class TC = A> class B;
+
+      The first time we see TF, we should not attempt to substitute
+      ARGLIST into it because ARGLIST is not complete yet. It will be
+      complete when all the template parameters of B are processed and
+      passed to end_template_parm_list to update
+      current_template_parms.
+
+      It's that point that ARGLIST will be [{Key, T, NULL} {NULL}] thus
+      suitable to be substituted into the default argument of TF.  */
+  if (!incomplete_args_for_template_parm_fixup_p (arglist,
+						  TREE_VALUE (parm_desc)))
+    TREE_PURPOSE (parm_desc) =
+      tsubst_template_arg (TREE_PURPOSE (parm_desc),
+			   arglist, tf_none, parm);
+ end:
   pop_deferring_access_checks ();
 }
 
-/* Walk current the template parms and properly compute the canonical
+/* 
+   This is a subroutine of fixup_template_parm.
+
+   Return TRUE if the full list of template arguments [a vector of
+   vector as described in the comments of
+   TMPL_ARGS_HAVE_MULTIPLE_LEVELS] is incomplete to be used to
+   substitute into the template parameter PARM, for the purpose of
+   fixing up PARM with fixup_template_parm.  ARGLIST is considered
+   complete if all the template levels preceding the level of PARM are
+   filled with arguments.  Please read the comments of the places of
+   this function is used to understand more.  */
+
+static bool
+incomplete_args_for_template_parm_fixup_p (tree arglist, tree parm)
+{
+  int i, level, index;
+  tree args;
+
+  if (TREE_CODE (parm) == TYPE_DECL
+      || TREE_CODE (parm) == TEMPLATE_DECL)
+    {
+      level = TEMPLATE_TYPE_LEVEL (TREE_TYPE (parm));
+      index = TEMPLATE_TYPE_IDX (TREE_TYPE (parm));
+    }
+  else if (TREE_CODE (parm) == PARM_DECL)
+    {
+      level = TEMPLATE_PARM_LEVEL (DECL_INITIAL (parm));
+      index = TEMPLATE_PARM_IDX (DECL_INITIAL (parm));
+    }
+  else
+    return false;
+
+  if (TMPL_ARGS_DEPTH (arglist) == 1
+      && index < 1)
+    /* Fixing up the first template parm should always be possible
+       with a template arguments set of depth 1.  */
+    return false;
+
+  for (i = 0; i < level - 1; ++i)
+    {
+      args = TREE_VEC_ELT (arglist, i);
+      gcc_assert (args != NULL_TREE);
+
+      if (TREE_CODE (args) == TREE_VEC
+	  && tree_vec_empty_p (args))
+	return true;
+    }
+
+  return false;
+}
+
+/* Walk the current template parms and properly compute the canonical
    types of the dependent types created during
    cp_parser_template_parameter_list.  */
 
@@ -3911,8 +4008,6 @@  fixup_template_parms (void)
   arglist = current_template_args ();
   arglist = add_outermost_template_args (arglist, fixedup_args);
 
-  fixedup_args = INNERMOST_TEMPLATE_ARGS (arglist);
-
   /* Let's do the proper fixup now.  */
   for (i = 0; i < num_parms; ++i)
     fixup_template_parm (TREE_VEC_ELT (parameter_vec, i),
diff --git a/gcc/testsuite/g++.dg/template/param2.C b/gcc/testsuite/g++.dg/template/param2.C
new file mode 100644
index 0000000..d25b855
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/param2.C
@@ -0,0 +1,8 @@ 
+// Origin PR c++/47311
+// { dg-do compile }
+
+template < typename > class A0;
+template <class Key, class T, template < typename TF = T> class TC = A0> class B0;
+
+template <int> class A1;
+template <class Key, class T, template <T p> class TC = A1> class B1;
diff --git a/gcc/tree.c b/gcc/tree.c
index be2cf98..9995300 100644
--- a/gcc/tree.c
+++ b/gcc/tree.c
@@ -1694,6 +1694,25 @@  make_tree_vec_stat (int len MEM_STAT_DECL)
 
   return t;
 }
+
+/* Return TRUE if T is a TREE_VEC filled with NULL_TREE or a TREE_VEC
+   of size zero.  */
+
+bool
+tree_vec_empty_p (const_tree t)
+{
+  int i;
+
+  if (t == NULL_TREE || TREE_CODE (t) != TREE_VEC)
+    return false;
+
+  for (i = 0; i < TREE_VEC_LENGTH (t); ++i)
+    if (TREE_VEC_ELT (t, i) != NULL_TREE)
+      return false;
+
+  return true;
+}
+
 
 /* Return 1 if EXPR is the integer constant zero or a complex constant
    of zero.  */
diff --git a/gcc/tree.h b/gcc/tree.h
index a49e335..a97cc20 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -3988,6 +3988,8 @@  extern tree make_tree_binfo_stat (unsigned MEM_STAT_DECL);
 extern tree make_tree_vec_stat (int MEM_STAT_DECL);
 #define make_tree_vec(t) make_tree_vec_stat (t MEM_STAT_INFO)
 
+bool tree_vec_empty_p (const_tree t);
+
 /* Return the (unique) IDENTIFIER_NODE node for a given name.
    The name is supplied as a char *.  */