C++ PATCH: specializations of member templates

Mark Mitchell mark@codesourcery.com
Sun Mar 12 10:51:00 GMT 2000


The following is legal C++:

  template <class T> struct S {
    template <class U> void f(U);
  };
  
  template <> template <class U> void S<int>::f(U) { ... };

This says that even though `S<int>' is just an instantiation of `S',
the `S<int>::f' template is a specialization: all instances thereof
get the special body.  We didn't support this.  Now we do.

As a side-effect, we now issue an error message on:

  template <class T> template <class T2> void f(T2) {}

which we didn't before.

I also cleaned up some portions of the code in the process.  I've
started generalizing the code in check_explicit_specialization so that
we can use it for classes as well as functions.  Also, I made a little
bit more progress towards using standard names for things whose names
are defined in the standard, and removed a large block of duplicated
code in grokfndecl.  (We handled constructors and normal functions
with two nearly-identical blocks that had to be manually kept in
sync.)

2000-03-11  Mark Mitchell  <mark@codesourcery.com>

	* cp-tree.h (scope_kind): New type.
	(tmpl_spec_kind): Likewise.
	(declare_pseudo_global_level): Remove.
	(pseudo_global_level_p): Rename to template_parm_scope_p.
	(pushlevel): Remove declaration.
	(begin_scope): New function.
	(finish_scope): Likewise.
	(current_tmpl_spec_kind): Likewise.
	* decl.c (struct binding_level): Shorten parm_flag to 2 bits.
	Shorten keep to 2 bits.  Rename pseudo_global to template_parms_p.
	Add template_spec_p.
	(toplevel_bindings_p): Adjust.
	(declare_pseudo_global_level): Remove.
	(pseudo_global_level_p): Rename to template_parm_scope_p.
	(current_tmpl_spec_kind): New function.
	(begin_scope): Likewise.
	(finish_scope): Likewise.
	(maybe_push_to_top_level): Adjust.
	(maybe_process_template_type_declaration): Likewise.
	(pushtag): Likewise.
	(pushdecl_nonclass_level): Likewise.
	(lookup_tag): Likewise.
	(grokfndecl): Handle member template specializations.  Share
	constructor and non-constructor code.
	* decl2.c (check_classfn): Handle member template specializations.
	* pt.c (begin_template_parm_list): Use begin_scope.
	(begin_specialization): Likewise.
	(end_specialization): Likewise.
	(check_explicit_specialization): Use current_tmpl_spec_kind.
	Handle member template specializations.
	(end_template_decl): Use finish_scope.  Remove call to
	get_pending_sizes.
	(push_template_decl_real): Remove bogus error message.
	(tsubst_decl): Fix typo in code contained in comment.
	(instantiate_template): Handle member template specializations.
	(most_general_template): Likewise.
	
Index: g++.benjamin/tem05.C
===================================================================
RCS file: /cvs/gcc/egcs/gcc/testsuite/g++.old-deja/g++.benjamin/tem05.C,v
retrieving revision 1.2
diff -c -p -r1.2 tem05.C
*** tem05.C	1998/12/16 21:22:17	1.2
--- tem05.C	2000/03/12 18:39:14
*************** unsigned short X_one<T>::ret_id() {
*** 49,55 ****
    return id;
  }
  
! export template <class T> template <class T2> // WARNING - 
  bool compare_ge(T2 test) {
    if (test > type)
      return true;
--- 49,55 ----
    return id;
  }
  
! export template <class T2> // WARNING - 
  bool compare_ge(T2 test) {
    if (test > type)
      return true;
Index: g++.pt/memtemp93.C
===================================================================
RCS file: memtemp93.C
diff -N memtemp93.C
*** /dev/null	Tue May  5 13:32:27 1998
--- memtemp93.C	Sun Mar 12 10:39:16 2000
***************
*** 0 ****
--- 1,16 ----
+ // Build don't run:
+ // Origin: Mark Mitchell <mark@codesourcery.com>
+ 
+ template <int n> struct A { 
+   template <class T> A (T t);
+   template <class T> int f(T t) const; 
+ }; 
+ 
+ template <> template<class T> int A<1>::f(T t) const {return 1;} 
+ template <> template<class T> A<1>::A (T t) {}
+ 
+ int main() { 
+   A<1> a (3);
+   a.f(1);
+   return 0; 
+ }
Index: cp-tree.h
===================================================================
RCS file: /cvs/gcc/egcs/gcc/cp/cp-tree.h,v
retrieving revision 1.415
diff -c -p -r1.415 cp-tree.h
*** cp-tree.h	2000/03/10 11:56:00	1.415
--- cp-tree.h	2000/03/12 18:35:42
*************** typedef enum cp_lvalue_kind {
*** 3043,3048 ****
--- 3043,3074 ----
    clk_bitfield = 4, /* An lvalue for a bit-field.  */
  } cp_lvalue_kind;
  
+ /* The kinds of scopes we recognize.  */
+ typedef enum scope_kind {
+   sk_template_parms, /* A scope for template parameters.  */
+   sk_template_spec   /* A scope corresponding to a template
+ 			specialization.  There is never anything in
+ 			this scope.  */
+ } scope_kind;
+ 
+ /* Various kinds of template specialization, instantiation, etc.  */
+ typedef enum tmpl_spec_kind {
+   tsk_none,                /* Not a template at all.  */
+   tsk_invalid_member_spec, /* An explicit member template
+ 			      specialization, but the enclosing
+ 			      classes have not all been explicitly
+ 			      specialized.  */
+   tsk_invalid_expl_inst,   /* An explicit instantiation containing
+ 			      template parameter lists.  */
+   tsk_excessive_parms,     /* A template declaration with too many
+ 			      template parameter lists.  */
+   tsk_insufficient_parms,  /* A template declaration with too few
+ 			      parameter lists.  */
+   tsk_template,            /* A template declaration.  */
+   tsk_expl_spec,           /* An explicit specialization.  */
+   tsk_expl_inst            /* An explicit instantiation.  */
+ } tmpl_spec_kind;
+ 
  /* Zero means prototype weakly, as in ANSI C (no args means nothing).
     Each language context defines how this variable should be set.  */
  extern int strict_prototype;
*************** extern int toplevel_bindings_p			PARAMS 
*** 3682,3691 ****
  extern int namespace_bindings_p			PARAMS ((void));
  extern void keep_next_level			PARAMS ((int));
  extern int kept_level_p				PARAMS ((void));
! extern void declare_pseudo_global_level		PARAMS ((void));
! extern int pseudo_global_level_p		PARAMS ((void));
  extern void set_class_shadows			PARAMS ((tree));
! extern void pushlevel				PARAMS ((int));
  extern void note_level_for_for			PARAMS ((void));
  extern void resume_level			PARAMS ((struct binding_level *));
  extern void delete_block			PARAMS ((tree));
--- 3708,3717 ----
  extern int namespace_bindings_p			PARAMS ((void));
  extern void keep_next_level			PARAMS ((int));
  extern int kept_level_p				PARAMS ((void));
! extern int template_parm_scope_p		PARAMS ((void));
  extern void set_class_shadows			PARAMS ((tree));
! extern void begin_scope                         PARAMS ((scope_kind));
! extern void finish_scope                        PARAMS ((void));
  extern void note_level_for_for			PARAMS ((void));
  extern void resume_level			PARAMS ((struct binding_level *));
  extern void delete_block			PARAMS ((tree));
*************** extern int local_variable_p             
*** 3832,3837 ****
--- 3858,3864 ----
  extern int nonstatic_local_decl_p               PARAMS ((tree));
  extern tree declare_global_var                  PARAMS ((tree, tree));
  extern void register_dtor_fn                    PARAMS ((tree));
+ extern tmpl_spec_kind current_tmpl_spec_kind    PARAMS ((int));
  
  /* in decl2.c */
  extern void init_decl2				PARAMS ((void));
Index: decl.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/cp/decl.c,v
retrieving revision 1.565
diff -c -p -r1.565 decl.c
*** decl.c	2000/03/11 08:27:17	1.565
--- decl.c	2000/03/12 18:35:51
*************** struct binding_level
*** 456,468 ****
      tree dead_vars_from_for;
  
      /* 1 for the level that holds the parameters of a function.
!        2 for the level that holds a class declaration.
!        3 for levels that hold parameter declarations.  */
!     unsigned parm_flag : 4;
  
      /* 1 means make a BLOCK for this level regardless of all else.
         2 for temporary binding contours created by the compiler.  */
!     unsigned keep : 3;
  
      /* Nonzero if this level "doesn't exist" for tags.  */
      unsigned tag_transparent : 1;
--- 456,467 ----
      tree dead_vars_from_for;
  
      /* 1 for the level that holds the parameters of a function.
!        2 for the level that holds a class declaration.  */
!     unsigned parm_flag : 2;
  
      /* 1 means make a BLOCK for this level regardless of all else.
         2 for temporary binding contours created by the compiler.  */
!     unsigned keep : 2;
  
      /* Nonzero if this level "doesn't exist" for tags.  */
      unsigned tag_transparent : 1;
*************** struct binding_level
*** 472,482 ****
      unsigned more_cleanups_ok : 1;
      unsigned have_cleanups : 1;
  
!     /* Nonzero if this level is for storing the decls for template
         parameters and generic decls; these decls will be discarded and
         replaced with a TEMPLATE_DECL.  */
!     unsigned pseudo_global : 1;
  
      /* This is set for a namespace binding level.  */
      unsigned namespace_p : 1;
  
--- 471,486 ----
      unsigned more_cleanups_ok : 1;
      unsigned have_cleanups : 1;
  
!     /* Nonzero if this scope is for storing the decls for template
         parameters and generic decls; these decls will be discarded and
         replaced with a TEMPLATE_DECL.  */
!     unsigned template_parms_p : 1;
  
+     /* Nonzero if this scope corresponds to the `<>' in a 
+        `template <>' clause.  Whenever this flag is set,
+        TEMPLATE_PARMS_P will be set as well.  */
+     unsigned template_spec_p : 1;
+ 
      /* This is set for a namespace binding level.  */
      unsigned namespace_p : 1;
  
*************** struct binding_level
*** 487,493 ****
      /* True if this level corresponds to an EH region, as for a try block.  */
      unsigned eh_region : 1;
  
!     /* One bit left for this word.  */
  
  #if defined(DEBUG_CP_BINDING_LEVELS)
      /* Binding depth at which this level began.  */
--- 491,497 ----
      /* True if this level corresponds to an EH region, as for a try block.  */
      unsigned eh_region : 1;
  
!     /* Four bits left for this word.  */
  
  #if defined(DEBUG_CP_BINDING_LEVELS)
      /* Binding depth at which this level began.  */
*************** innermost_nonclass_level ()
*** 704,718 ****
  /* Nonzero if we are currently in a toplevel binding level.  This
     means either the global binding level or a namespace in a toplevel
     binding level.  Since there are no non-toplevel namespace levels,
!    this really means any namespace or pseudo-global level.  We also
!    include a class whose context is toplevel.  */
  
  int
  toplevel_bindings_p ()
  {
    struct binding_level *b = innermost_nonclass_level ();
  
!   return b->namespace_p || b->pseudo_global;
  }
  
  /* Nonzero if this is a namespace scope, or if we are defining a class
--- 708,722 ----
  /* Nonzero if we are currently in a toplevel binding level.  This
     means either the global binding level or a namespace in a toplevel
     binding level.  Since there are no non-toplevel namespace levels,
!    this really means any namespace or template parameter level.  We
!    also include a class whose context is toplevel.  */
  
  int
  toplevel_bindings_p ()
  {
    struct binding_level *b = innermost_nonclass_level ();
  
!   return b->namespace_p || b->template_parms_p;
  }
  
  /* Nonzero if this is a namespace scope, or if we are defining a class
*************** kept_level_p ()
*** 750,773 ****
  	      && !current_binding_level->tag_transparent));
  }
  
- void
- declare_pseudo_global_level ()
- {
-   current_binding_level->pseudo_global = 1;
- }
- 
  static void
  declare_namespace_level ()
  {
    current_binding_level->namespace_p = 1;
  }
  
  int
! pseudo_global_level_p ()
  {
!   return current_binding_level->pseudo_global;
  }
  
  void
  set_class_shadows (shadows)
       tree shadows;
--- 754,863 ----
  	      && !current_binding_level->tag_transparent));
  }
  
  static void
  declare_namespace_level ()
  {
    current_binding_level->namespace_p = 1;
  }
  
+ /* Returns non-zero if this scope was created to store template
+    parameters.  */
+ 
  int
! template_parm_scope_p ()
  {
!   return current_binding_level->template_parms_p;
  }
  
+ /* Returns the kind of template specialization we are currently
+    processing, given that it's declaration contained N_CLASS_SCOPES
+    explicit scope qualifications.  */
+ 
+ tmpl_spec_kind
+ current_tmpl_spec_kind (n_class_scopes)
+      int n_class_scopes;
+ {
+   int n_template_parm_scopes = 0;
+   int seen_specialization_p = 0;
+   int innermost_specialization_p = 0;
+   struct binding_level *b;
+ 
+   /* Scan through the template parameter scopes.  */
+   for (b = current_binding_level; b->template_parms_p; b = b->level_chain)
+     {
+       /* If we see a specialization scope inside a parameter scope,
+ 	 then something is wrong.  That corresponds to a declaration
+ 	 like:
+ 
+ 	    template <class T> template <> ...
+ 
+ 	 which is always illegal since [temp.expl.spec] forbids the
+ 	 specialization of a class member template if the enclosing
+ 	 class templates are not explicitly specialized as well.  */
+       if (b->template_spec_p)
+ 	{
+ 	  if (n_template_parm_scopes == 0)
+ 	    innermost_specialization_p = 1;
+ 	  else
+ 	    seen_specialization_p = 1;
+ 	}
+       else if (seen_specialization_p == 1)
+ 	return tsk_invalid_member_spec;
+ 
+       ++n_template_parm_scopes;
+     }
+ 
+   /* Handle explicit instantiations.  */
+   if (processing_explicit_instantiation)
+     {
+       if (n_template_parm_scopes != 0)
+ 	/* We've seen a template parameter list during an explicit
+ 	   instantiation.  For example:
+ 
+ 	     template <class T> template void f(int);
+ 
+ 	   This is erroneous.  */
+ 	return tsk_invalid_expl_inst;
+       else
+ 	return tsk_expl_inst;
+     }
+ 
+   if (n_template_parm_scopes < n_class_scopes)
+     /* We've not seen enough template headers to match all the
+        specialized classes present.  For example:
+ 
+          template <class T> void R<T>::S<T>::f(int);
+ 
+        This is illegal; there needs to be one set of template
+        parameters for each class.  */
+     return tsk_insufficient_parms;
+   else if (n_template_parm_scopes == n_class_scopes)
+     /* We're processing a non-template declaration (even though it may
+        be a member of a template class.)  For example:
+ 
+          template <class T> void S<T>::f(int);
+ 
+        The `class T' maches the `S<T>', leaving no template headers
+        corresponding to the `f'.  */
+     return tsk_none;
+   else if (n_template_parm_scopes > n_class_scopes + 1)
+     /* We've got too many template headers.  For example:
+ 
+          template <> template <class T> void f (T);
+ 
+        There need to be more enclosing classes.  */
+     return tsk_excessive_parms;
+   else
+     /* This must be a template.  It's of the form:
+ 
+          template <class T> template <class U> void S<T>::f(U);
+ 
+        This is a specialization if the innermost level was a
+        specialization; otherwise it's just a definition of the
+        template.  */
+     return innermost_specialization_p ? tsk_expl_spec : tsk_template;
+ }
+ 
  void
  set_class_shadows (shadows)
       tree shadows;
*************** pushlevel (tag_transparent)
*** 806,812 ****
--- 896,934 ----
    keep_next_level_flag = 0;
  }
  
+ /* Enter a new scope.  The KIND indicates what kind of scope is being
+    created.  */
+ 
+ void
+ begin_scope (sk)
+      scope_kind sk;
+ {
+   pushlevel (0);
+ 
+   switch (sk)
+     {
+     case sk_template_spec:
+       current_binding_level->template_spec_p = 1;
+       /* Fall through.  */
+ 
+     case sk_template_parms:
+       current_binding_level->template_parms_p = 1;
+       break;
+ 
+     default:
+       my_friendly_abort (20000309);
+     }
+ }
+ 
+ /* Exit the current scope.  */
+ 
  void
+ finish_scope ()
+ {
+   poplevel (0, 0, 0);
+ }
+ 
+ void
  note_level_for_for ()
  {
    current_binding_level->is_for_scope = 1;
*************** maybe_push_to_top_level (pseudo)
*** 2381,2387 ****
  	 inserted into namespace level, finish_file wouldn't find them
  	 when doing pending instantiations. Therefore, don't stop at
  	 namespace level, but continue until :: .  */
!       if (b == global_binding_level || (pseudo && b->pseudo_global))
  	break;
  
        old_bindings = store_bindings (b->names, old_bindings);
--- 2503,2509 ----
  	 inserted into namespace level, finish_file wouldn't find them
  	 when doing pending instantiations. Therefore, don't stop at
  	 namespace level, but continue until :: .  */
!       if (b == global_binding_level || (pseudo && b->template_parms_p))
  	break;
  
        old_bindings = store_bindings (b->names, old_bindings);
*************** maybe_process_template_type_declaration 
*** 2590,2596 ****
  	     friend case, push_template_decl will already have put the
  	     friend into global scope, if appropriate.  */
  	  if (TREE_CODE (type) != ENUMERAL_TYPE
! 	      && !globalize && b->pseudo_global
  	      && b->level_chain->parm_flag == 2)
  	    {
  	      finish_member_declaration (CLASSTYPE_TI_TEMPLATE (type));
--- 2712,2718 ----
  	     friend case, push_template_decl will already have put the
  	     friend into global scope, if appropriate.  */
  	  if (TREE_CODE (type) != ENUMERAL_TYPE
! 	      && !globalize && b->template_parms_p
  	      && b->level_chain->parm_flag == 2)
  	    {
  	      finish_member_declaration (CLASSTYPE_TI_TEMPLATE (type));
*************** pushtag (name, type, globalize)
*** 2675,2681 ****
  	  if (!context)
  	    context = current_namespace;
  
! 	  if ((b->pseudo_global && b->level_chain->parm_flag == 2)
  	      || b->parm_flag == 2)
  	    in_class = 1;
  
--- 2797,2803 ----
  	  if (!context)
  	    context = current_namespace;
  
! 	  if ((b->template_parms_p && b->level_chain->parm_flag == 2)
  	      || b->parm_flag == 2)
  	    in_class = 1;
  
*************** maybe_push_decl (decl)
*** 4195,4223 ****
      return pushdecl (decl);
  }
  
- #if 0
- /* This function is used to push the mangled decls for nested types into
-    the appropriate scope.  Previously pushdecl_top_level was used, but that
-    is incorrect for members of local classes.  */
- 
- void
- pushdecl_nonclass_level (x)
-      tree x;
- {
-   struct binding_level *b = current_binding_level;
- 
-   my_friendly_assert (b->parm_flag != 2, 180);
- 
- #if 0
-   /* Get out of template binding levels */
-   while (b->pseudo_global)
-     b = b->level_chain;
- #endif
- 
-   pushdecl_with_scope (x, b);
- }
- #endif
- 
  /* Make the declaration(s) of X appear in CLASS scope
     under the name NAME.  */
  
--- 4317,4322 ----
*************** lookup_tag (form, name, binding_level, t
*** 4969,4977 ****
       int thislevel_only;
  {
    register struct binding_level *level;
!   /* Non-zero if, we should look past a pseudo-global level, even if
!      THISLEVEL_ONLY.  */
!   int allow_pseudo_global = 1;
  
    for (level = binding_level; level; level = level->level_chain)
      {
--- 5068,5076 ----
       int thislevel_only;
  {
    register struct binding_level *level;
!   /* Non-zero if, we should look past a template parameter level, even
!      if THISLEVEL_ONLY.  */
!   int allow_template_parms_p = 1;
  
    for (level = binding_level; level; level = level->level_chain)
      {
*************** lookup_tag (form, name, binding_level, t
*** 4990,5000 ****
  	  {
  	    tree old = binding_for_name (name, tail);
  
! 	    /* If we just skipped past a pseudo global level, even
! 	       though THISLEVEL_ONLY, and we find a template class
! 	       declaration, then we use the _TYPE node for the
  	       template.  See the example below.  */
! 	    if (thislevel_only && !allow_pseudo_global
  		&& old && BINDING_VALUE (old)
  		&& DECL_CLASS_TEMPLATE_P (BINDING_VALUE (old)))
  	      old = TREE_TYPE (BINDING_VALUE (old));
--- 5089,5099 ----
  	  {
  	    tree old = binding_for_name (name, tail);
  
! 	    /* If we just skipped past a template parameter level,
! 	       even though THISLEVEL_ONLY, and we find a template
! 	       class declaration, then we use the _TYPE node for the
  	       template.  See the example below.  */
! 	    if (thislevel_only && !allow_template_parms_p
  		&& old && BINDING_VALUE (old)
  		&& DECL_CLASS_TEMPLATE_P (BINDING_VALUE (old)))
  	      old = TREE_TYPE (BINDING_VALUE (old));
*************** lookup_tag (form, name, binding_level, t
*** 5037,5043 ****
  	  }
        if (thislevel_only && ! level->tag_transparent)
  	{
! 	  if (level->pseudo_global && allow_pseudo_global)
  	    {
  	      /* We must deal with cases like this:
  
--- 5136,5142 ----
  	  }
        if (thislevel_only && ! level->tag_transparent)
  	{
! 	  if (level->template_parms_p && allow_template_parms_p)
  	    {
  	      /* We must deal with cases like this:
  
*************** lookup_tag (form, name, binding_level, t
*** 5050,5056 ****
  		 template parameters, rather than the (surrounding)
  		 namespace level.  Thus, we keep going one more level,
  		 even though THISLEVEL_ONLY is non-zero.  */
! 	      allow_pseudo_global = 0;
  	      continue;
  	    }
  	  else
--- 5149,5155 ----
  		 template parameters, rather than the (surrounding)
  		 namespace level.  Thus, we keep going one more level,
  		 even though THISLEVEL_ONLY is non-zero.  */
! 	      allow_template_parms_p = 0;
  	      continue;
  	    }
  	  else
*************** grokfndecl (ctype, type, declarator, ori
*** 8680,8777 ****
      return decl;
  
    if (flags == NO_SPECIAL && ctype && constructor_name (cname) == declarator)
!     {
!       tree tmp;
!       /* Just handle constructors here.  We could do this
! 	 inside the following if stmt, but I think
! 	 that the code is more legible by breaking this
! 	 case out.  See comments below for what each of
! 	 the following calls is supposed to do.  */
!       DECL_CONSTRUCTOR_P (decl) = 1;
! 
!       grokclassfn (ctype, decl, flags, quals);
! 
!       decl = check_explicit_specialization (orig_declarator, decl,
! 					    template_count,
! 					    2 * (funcdef_flag != 0) +
! 					    4 * (friendp != 0));
!       if (decl == error_mark_node)
! 	return NULL_TREE;
! 
!       if ((! TYPE_FOR_JAVA (ctype) || check_java_method (decl))
! 	  && check)
! 	{
! 	  tmp = check_classfn (ctype, decl);
  
! 	  if (tmp && TREE_CODE (tmp) == TEMPLATE_DECL)
! 	    tmp = DECL_TEMPLATE_RESULT(tmp);
  
! 	  if (tmp && DECL_ARTIFICIAL (tmp))
! 	    cp_error ("definition of implicitly-declared `%D'", tmp);
! 	  if (tmp && duplicate_decls (decl, tmp))
! 	    return tmp;
! 	}
!       if (! grok_ctor_properties (ctype, decl))
! 	return NULL_TREE;
!     }
!   else
      {
!       tree tmp;
  
!       /* Function gets the ugly name, field gets the nice one.
! 	 This call may change the type of the function (because
! 	 of default parameters)!  */
!       if (ctype != NULL_TREE)
! 	grokclassfn (ctype, decl, flags, quals);
! 
!       decl = check_explicit_specialization (orig_declarator, decl,
! 					    template_count,
! 					    2 * (funcdef_flag != 0) +
! 					    4 * (friendp != 0));
!       if (decl == error_mark_node)
! 	return NULL_TREE;
  
!       if (ctype != NULL_TREE
! 	  && (! TYPE_FOR_JAVA (ctype) || check_java_method (decl))
! 	  && check)
  	{
! 	  tmp = check_classfn (ctype, decl);
  
! 	  if (tmp && TREE_CODE (tmp) == TEMPLATE_DECL)
! 	    tmp = DECL_TEMPLATE_RESULT (tmp);
  
! 	  if (tmp && DECL_STATIC_FUNCTION_P (tmp)
! 	      && TREE_CODE (TREE_TYPE (decl)) == METHOD_TYPE)
! 	    {
! 	      /* Remove the `this' parm added by grokclassfn.
! 	         XXX Isn't this done in start_function, too?  */
! 	      revert_static_member_fn (&decl, NULL, NULL);
! 	      last_function_parms = TREE_CHAIN (last_function_parms);
! 	    }
! 	  if (tmp && DECL_ARTIFICIAL (tmp))
! 	    cp_error ("definition of implicitly-declared `%D'", tmp);
! 	  if (tmp)
! 	    {
! 	      /* Attempt to merge the declarations.  This can fail, in
! 		 the case of some illegal specialization declarations.  */
! 	      if (!duplicate_decls (decl, tmp))
! 		cp_error ("no `%#D' member function declared in class `%T'",
! 			  decl, ctype);
! 	      return tmp;
! 	    }
  	}
  
!       if (ctype == NULL_TREE || check)
! 	return decl;
  
!       if (virtualp)
! 	{
! 	  DECL_VIRTUAL_P (decl) = 1;
! 	  if (DECL_VINDEX (decl) == NULL_TREE)
! 	    DECL_VINDEX (decl) = error_mark_node;
! 	  IDENTIFIER_VIRTUAL_P (DECL_NAME (decl)) = 1;
! 	}
      }
    return decl;
  }
  
--- 8779,8855 ----
      return decl;
  
    if (flags == NO_SPECIAL && ctype && constructor_name (cname) == declarator)
!     DECL_CONSTRUCTOR_P (decl) = 1;
  
!   /* Function gets the ugly name, field gets the nice one.  This call
!      may change the type of the function (because of default
!      parameters)!  */
!   if (ctype != NULL_TREE)
!     grokclassfn (ctype, decl, flags, quals);
! 
!   decl = check_explicit_specialization (orig_declarator, decl,
! 					template_count,
! 					2 * (funcdef_flag != 0) +
! 					4 * (friendp != 0));
!   if (decl == error_mark_node)
!     return NULL_TREE;
  
!   if (ctype != NULL_TREE
!       && (! TYPE_FOR_JAVA (ctype) || check_java_method (decl))
!       && check)
      {
!       tree old_decl;
  
!       old_decl = check_classfn (ctype, decl);
! 
!       if (old_decl && TREE_CODE (old_decl) == TEMPLATE_DECL)
! 	/* Because grokfndecl is always supposed to return a
! 	   FUNCTION_DECL, we pull out the DECL_TEMPLATE_RESULT
! 	   here.  We depend on our callers to figure out that its
! 	   really a template that's being returned.  */
! 	old_decl = DECL_TEMPLATE_RESULT (old_decl);
  
!       if (old_decl && DECL_STATIC_FUNCTION_P (old_decl)
! 	  && TREE_CODE (TREE_TYPE (decl)) == METHOD_TYPE)
  	{
! 	  /* Remove the `this' parm added by grokclassfn.
! 	     XXX Isn't this done in start_function, too?  */
! 	  revert_static_member_fn (&decl, NULL, NULL);
! 	  last_function_parms = TREE_CHAIN (last_function_parms);
! 	}
!       if (old_decl && DECL_ARTIFICIAL (old_decl))
! 	cp_error ("definition of implicitly-declared `%D'", old_decl);
  
!       if (old_decl)
! 	{
! 	  /* Since we've smashed OLD_DECL to its
! 	     DECL_TEMPLATE_RESULT, we must do the same to DECL.  */
! 	  if (TREE_CODE (decl) == TEMPLATE_DECL)
! 	    decl = DECL_TEMPLATE_RESULT (decl);
  
! 	  /* Attempt to merge the declarations.  This can fail, in
! 	     the case of some illegal specialization declarations.  */
! 	  if (!duplicate_decls (decl, old_decl))
! 	    cp_error ("no `%#D' member function declared in class `%T'",
! 		      decl, ctype);
! 	  return old_decl;
  	}
+     }
  
!   if (DECL_CONSTRUCTOR_P (decl) && !grok_ctor_properties (ctype, decl))
!     return NULL_TREE;
  
!   if (ctype == NULL_TREE || check)
!     return decl;
! 
!   if (virtualp)
!     {
!       DECL_VIRTUAL_P (decl) = 1;
!       if (DECL_VINDEX (decl) == NULL_TREE)
! 	DECL_VINDEX (decl) = error_mark_node;
!       IDENTIFIER_VIRTUAL_P (DECL_NAME (decl)) = 1;
      }
+ 
    return decl;
  }
  
Index: decl2.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/cp/decl2.c,v
retrieving revision 1.317
diff -c -p -r1.317 decl2.c
*** decl2.c	2000/03/10 11:56:00	1.317
--- decl2.c	2000/03/12 18:35:54
*************** check_classfn (ctype, function)
*** 1358,1363 ****
--- 1358,1365 ----
    tree *end = 0;
    
    if (DECL_USE_TEMPLATE (function)
+       && !(TREE_CODE (function) == TEMPLATE_DECL
+ 	   && DECL_TEMPLATE_SPECIALIZATION (function))
        && is_member_template (DECL_TI_TEMPLATE (function)))
      /* Since this is a specialization of a member template,
         we're not going to find the declaration in the class.
Index: pt.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/cp/pt.c,v
retrieving revision 1.404
diff -c -p -r1.404 pt.c
*** pt.c	2000/03/11 00:23:18	1.404
--- pt.c	2000/03/12 18:36:00
*************** begin_template_parm_list ()
*** 551,558 ****
  
       pushtag contains special code to call pushdecl_with_scope on the
       TEMPLATE_DECL for S2.  */
!   pushlevel (0);
!   declare_pseudo_global_level ();
    ++processing_template_decl;
    ++processing_template_parmlist;
    note_template_header (0);
--- 551,557 ----
  
       pushtag contains special code to call pushdecl_with_scope on the
       TEMPLATE_DECL for S2.  */
!   begin_scope (sk_template_parms);
    ++processing_template_decl;
    ++processing_template_parmlist;
    note_template_header (0);
*************** check_specialization_scope ()
*** 596,601 ****
--- 595,601 ----
  void
  begin_specialization ()
  {
+   begin_scope (sk_template_spec);
    note_template_header (1);
    check_specialization_scope ();
  }
*************** begin_specialization ()
*** 606,611 ****
--- 606,612 ----
  void 
  end_specialization ()
  {
+   finish_scope ();
    reset_specialization ();
  }
  
*************** check_explicit_specialization (declarato
*** 1166,1274 ****
    int specialization = 0;
    int explicit_instantiation = 0;
    int member_specialization = 0;
- 
    tree ctype = DECL_CLASS_CONTEXT (decl);
    tree dname = DECL_NAME (decl);
  
!   if (processing_specialization) 
!     {
!       /* The last template header was of the form template <>.  */
! 	  
!       if (template_header_count > template_count) 
! 	{
! 	  /* There were more template headers than qualifying template
! 	     classes.  */
! 	  if (template_header_count - template_count > 1)
! 	    /* There shouldn't be that many template parameter lists.
! 	       There can be at most one parameter list for every
! 	       qualifying class, plus one for the function itself.  */
! 	    cp_error ("too many template parameter lists in declaration of `%D'", decl);
  
! 	  SET_DECL_TEMPLATE_SPECIALIZATION (decl);
! 	  if (ctype)
! 	    member_specialization = 1;
! 	  else
! 	    specialization = 1;
! 	}
!       else if (template_header_count == template_count)
  	{
- 	  /* The counts are equal.  So, this might be a
- 	     specialization, but it is not a specialization of a
- 	     member template.  It might be something like
- 		 
- 	     template <class T> struct S { 
- 	     void f(int i); 
- 	     };
- 	     template <>
- 	     void S<int>::f(int i) {}  */
  	  specialization = 1;
  	  SET_DECL_TEMPLATE_SPECIALIZATION (decl);
  	}
!       else 
  	{
! 	  /* This cannot be an explicit specialization.  There are not
! 	     enough headers for all of the qualifying classes.  For
! 	     example, we might have:
! 	     
! 	     template <>
! 	     void S<int>::T<char>::f();
  
! 	     But, we're missing another template <>.  */
! 	  cp_error("too few template parameter lists in declaration of `%D'", decl);
! 	  return decl;
! 	} 
!     }
!   else if (processing_explicit_instantiation)
!     {
!       if (template_header_count)
! 	cp_error ("template parameter list used in explicit instantiation");
! 	  
        if (have_def)
  	cp_error ("definition provided for explicit instantiation");
! 
        explicit_instantiation = 1;
!     }
!   else if (ctype != NULL_TREE
! 	   && !TYPE_BEING_DEFINED (ctype)
! 	   && CLASSTYPE_TEMPLATE_INSTANTIATION (ctype)
! 	   && !is_friend)
!     {
!       /* This case catches outdated code that looks like this:
! 
! 	 template <class T> struct S { void f(); };
! 	 void S<int>::f() {} // Missing template <>
! 
! 	 We disable this check when the type is being defined to
! 	 avoid complaining about default compiler-generated
! 	 constructors, destructors, and assignment operators.
! 	 Since the type is an instantiation, not a specialization,
! 	 these are the only functions that can be defined before
! 	 the class is complete.  */
! 
! 	  /* If they said
! 	       template <class T> void S<int>::f() {}
! 	     that's bogus.  */
        if (template_header_count)
  	{
! 	  cp_error ("template parameters specified in specialization");
  	  return decl;
  	}
  
!       if (pedantic)
! 	cp_pedwarn
! 	  ("explicit specialization not preceded by `template <>'");
!       specialization = 1;
!       SET_DECL_TEMPLATE_SPECIALIZATION (decl);
!     }
!   else if (TREE_CODE (declarator) == TEMPLATE_ID_EXPR)
!     {
!       if (is_friend)
! 	/* This could be something like:
  
! 	   template <class T> void f(T);
! 	   class S { friend void f<>(int); }  */
! 	specialization = 1;
!       else
  	{
  	  /* This case handles bogus declarations like template <>
  	     template <class T> void f<int>(); */
--- 1167,1265 ----
    int specialization = 0;
    int explicit_instantiation = 0;
    int member_specialization = 0;
    tree ctype = DECL_CLASS_CONTEXT (decl);
    tree dname = DECL_NAME (decl);
+   tmpl_spec_kind tsk;
  
!   tsk = current_tmpl_spec_kind (template_count);
  
!   switch (tsk)
!     {
!     case tsk_none:
!       if (processing_specialization) 
  	{
  	  specialization = 1;
  	  SET_DECL_TEMPLATE_SPECIALIZATION (decl);
  	}
!       else if (TREE_CODE (declarator) == TEMPLATE_ID_EXPR)
  	{
! 	  if (is_friend)
! 	    /* This could be something like:
  
! 	       template <class T> void f(T);
! 	       class S { friend void f<>(int); }  */
! 	    specialization = 1;
! 	  else
! 	    {
! 	      /* This case handles bogus declarations like template <>
! 		 template <class T> void f<int>(); */
! 
! 	      cp_error ("template-id `%D' in declaration of primary template",
! 			declarator);
! 	      return decl;
! 	    }
! 	}
!       break;
! 
!     case tsk_invalid_member_spec:
!       /* The error has already been reported in
! 	 check_specialization_scope.  */
!       return error_mark_node;
! 
!     case tsk_invalid_expl_inst:
!       cp_error ("template parameter list used in explicit instantiation");
! 
!       /* Fall through.  */
! 
!     case tsk_expl_inst:
        if (have_def)
  	cp_error ("definition provided for explicit instantiation");
!       
        explicit_instantiation = 1;
!       break;
! 
!     case tsk_excessive_parms:
!       cp_error ("too many template parameter lists in declaration of `%D'", 
! 		decl);
!       return error_mark_node;
! 
!       /* Fall through.  */
!     case tsk_expl_spec:
!       SET_DECL_TEMPLATE_SPECIALIZATION (decl);
!       if (ctype)
! 	member_specialization = 1;
!       else
! 	specialization = 1;
!       break;
!      
!     case tsk_insufficient_parms:
        if (template_header_count)
  	{
! 	  cp_error("too few template parameter lists in declaration of `%D'", 
! 		   decl);
  	  return decl;
  	}
+       else if (ctype != NULL_TREE
+ 	       && !TYPE_BEING_DEFINED (ctype)
+ 	       && CLASSTYPE_TEMPLATE_INSTANTIATION (ctype)
+ 	       && !is_friend)
+ 	{
+ 	  /* For backwards compatibility, we accept:
  
! 	       template <class T> struct S { void f(); };
! 	       void S<int>::f() {} // Missing template <>
  
! 	     That used to be legal C++.  */
! 	  if (pedantic)
! 	    cp_pedwarn
! 	      ("explicit specialization not preceded by `template <>'");
! 	  specialization = 1;
! 	  SET_DECL_TEMPLATE_SPECIALIZATION (decl);
! 	}
!       break;
! 
!     case tsk_template:
!       if (TREE_CODE (declarator) == TEMPLATE_ID_EXPR)
  	{
  	  /* This case handles bogus declarations like template <>
  	     template <class T> void f<int>(); */
*************** check_explicit_specialization (declarato
*** 1277,1282 ****
--- 1268,1289 ----
  		    declarator);
  	  return decl;
  	}
+ 
+       if (ctype && CLASSTYPE_TEMPLATE_INSTANTIATION (ctype))
+ 	/* This is a specialization of a member template, without
+ 	   specialization the containing class.  Something like:
+ 
+ 	     template <class T> struct S {
+ 	       template <class U> void f (U); 
+              };
+ 	     template <> template <class U> void S<int>::f(U) {}
+ 	     
+ 	   That's a specialization -- but of the entire template.  */
+ 	specialization = 1;
+       break;
+ 
+     default:
+       my_friendly_abort (20000309);
      }
  
    if (specialization || member_specialization)
*************** check_explicit_specialization (declarato
*** 1474,1483 ****
  		  targs = new_targs;
  		}
  		  
! 	      decl = instantiate_template (tmpl, targs);
! 	      return decl;
  	    }
! 	  
  	  /* If we though that the DECL was a member function, but it
  	     turns out to be specializing a static member function,
  	     make DECL a static member function as well.  */
--- 1481,1499 ----
  		  targs = new_targs;
  		}
  		  
! 	      return instantiate_template (tmpl, targs);
  	    }
! 
! 	  /* If this is both a template specialization, then it's a
! 	     specialization of a member template of a template class.
! 	     In that case we want to return the TEMPLATE_DECL, not the
! 	     specialization of it.  */
! 	  if (tsk == tsk_template)
! 	    {
! 	      SET_DECL_TEMPLATE_SPECIALIZATION (tmpl);
! 	      return tmpl;
! 	    }
! 
  	  /* If we though that the DECL was a member function, but it
  	     turns out to be specializing a static member function,
  	     make DECL a static member function as well.  */
*************** check_explicit_specialization (declarato
*** 1504,1510 ****
  	     we do not mangle S<int>::f() here.  That's because it's
  	     just an ordinary member function and doesn't need special
  	     treatment.  We do this here so that the ordinary,
! 	     non-template, name-mangling algorith will not be used
  	     later.  */
  	  if ((is_member_template (tmpl) || ctype == NULL_TREE)
  	      && name_mangling_version >= 1)
--- 1520,1526 ----
  	     we do not mangle S<int>::f() here.  That's because it's
  	     just an ordinary member function and doesn't need special
  	     treatment.  We do this here so that the ordinary,
! 	     non-template, name-mangling algorithm will not be used
  	     later.  */
  	  if ((is_member_template (tmpl) || ctype == NULL_TREE)
  	      && name_mangling_version >= 1)
*************** end_template_decl ()
*** 1863,1873 ****
      return;
  
    /* This matches the pushlevel in begin_template_parm_list.  */
!   poplevel (0, 0, 0);
  
    --processing_template_decl;
    current_template_parms = TREE_CHAIN (current_template_parms);
-   (void) get_pending_sizes ();	/* Why? */
  }
  
  /* Given a template argument vector containing the template PARMS.
--- 1879,1888 ----
      return;
  
    /* This matches the pushlevel in begin_template_parm_list.  */
!   finish_scope ();
  
    --processing_template_decl;
    current_template_parms = TREE_CHAIN (current_template_parms);
  }
  
  /* Given a template argument vector containing the template PARMS.
*************** push_template_decl_real (decl, is_friend
*** 2370,2376 ****
      DECL_CONTEXT (decl) = FROB_CONTEXT (current_namespace);
  
    /* See if this is a primary template.  */
!   primary = pseudo_global_level_p ();
  
    if (primary)
      {
--- 2385,2391 ----
      DECL_CONTEXT (decl) = FROB_CONTEXT (current_namespace);
  
    /* See if this is a primary template.  */
!   primary = template_parm_scope_p ();
  
    if (primary)
      {
*************** push_template_decl_real (decl, is_friend
*** 2444,2452 ****
        tree a, t, current, parms;
        int i;
  
-       if (CLASSTYPE_TEMPLATE_INSTANTIATION (ctx))
- 	cp_error ("must specialize `%#T' before defining member `%#D'",
- 		  ctx, decl);
        if (TREE_CODE (decl) == TYPE_DECL)
  	{
  	  if ((IS_AGGR_TYPE_CODE (TREE_CODE (TREE_TYPE (decl)))
--- 2459,2464 ----
*************** tsubst_decl (t, args, type, in_decl)
*** 5601,5607 ****
  		 template <class T> struct S { 
  		   template <class U> friend void f();
  		 };
! 		 template <class U> friend void f() {}
  		 template S<int>;
  		 template void f<double>();
  
--- 5613,5619 ----
  		 template <class T> struct S { 
  		   template <class U> friend void f();
  		 };
! 		 template <class U> void f() {}
  		 template S<int>;
  		 template void f<double>();
  
*************** instantiate_template (tmpl, targ_ptr)
*** 7399,7405 ****
    if (spec != NULL_TREE)
      return spec;
  
!   if (DECL_TEMPLATE_INFO (tmpl))
      {
        /* The TMPL is a partial instantiation.  To get a full set of
  	 arguments we must add the arguments used to perform the
--- 7411,7417 ----
    if (spec != NULL_TREE)
      return spec;
  
!   if (DECL_TEMPLATE_INFO (tmpl) && !DECL_TEMPLATE_SPECIALIZATION (tmpl))
      {
        /* The TMPL is a partial instantiation.  To get a full set of
  	 arguments we must add the arguments used to perform the
*************** most_general_template (decl)
*** 8948,8953 ****
--- 8960,8967 ----
       tree decl;
  {
    while (DECL_TEMPLATE_INFO (decl)
+ 	 && !(TREE_CODE (decl) == TEMPLATE_DECL
+ 	      && DECL_TEMPLATE_SPECIALIZATION (decl))
  	 /* The DECL_TI_TEMPLATE can be a LOOKUP_EXPR or
  	    IDENTIFIER_NODE in some cases.  (See cp-tree.h for
  	    details.)  */


More information about the Gcc-patches mailing list