diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index 37be278..0e4812c 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -1,3 +1,42 @@
+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.
+	
 2000-03-11  Gabriel Dos Reis  <gdr@codesourcery.com>
 
 	* lex.c (whitespace_cr): Compress consecutive calls to warning().
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 9c348a0..28f67c0 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -3043,6 +3043,32 @@ typedef enum cp_lvalue_kind {
   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;
@@ -3682,10 +3708,10 @@ extern int toplevel_bindings_p			PARAMS ((void));
 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 int template_parm_scope_p		PARAMS ((void));
 extern void set_class_shadows			PARAMS ((tree));
-extern void pushlevel				PARAMS ((int));
+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));
@@ -3832,6 +3858,7 @@ extern int local_variable_p                     PARAMS ((tree));
 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));
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index 20a6aba..5b6e6a9 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -456,13 +456,12 @@ struct binding_level
     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;
+       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 : 3;
+    unsigned keep : 2;
 
     /* Nonzero if this level "doesn't exist" for tags.  */
     unsigned tag_transparent : 1;
@@ -472,10 +471,15 @@ struct binding_level
     unsigned more_cleanups_ok : 1;
     unsigned have_cleanups : 1;
 
-    /* Nonzero if this level is for storing the decls for template
+    /* 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 pseudo_global : 1;
+    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;
@@ -487,7 +491,7 @@ struct binding_level
     /* True if this level corresponds to an EH region, as for a try block.  */
     unsigned eh_region : 1;
 
-    /* One bit left for this word.  */
+    /* Four bits left for this word.  */
 
 #if defined(DEBUG_CP_BINDING_LEVELS)
     /* Binding depth at which this level began.  */
@@ -704,15 +708,15 @@ innermost_nonclass_level ()
 /* 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.  */
+   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->pseudo_global;
+  return b->namespace_p || b->template_parms_p;
 }
 
 /* Nonzero if this is a namespace scope, or if we are defining a class
@@ -750,22 +754,108 @@ kept_level_p ()
 	      && !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;
 }
 
+/* Returns non-zero if this scope was created to store template
+   parameters.  */
+
 int
-pseudo_global_level_p ()
+template_parm_scope_p ()
 {
-  return current_binding_level->pseudo_global;
+  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
@@ -806,6 +896,38 @@ pushlevel (tag_transparent)
   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 ()
 {
@@ -2381,7 +2503,7 @@ maybe_push_to_top_level (pseudo)
 	 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))
+      if (b == global_binding_level || (pseudo && b->template_parms_p))
 	break;
 
       old_bindings = store_bindings (b->names, old_bindings);
@@ -2590,7 +2712,7 @@ maybe_process_template_type_declaration (type, globalize, b)
 	     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
+	      && !globalize && b->template_parms_p
 	      && b->level_chain->parm_flag == 2)
 	    {
 	      finish_member_declaration (CLASSTYPE_TI_TEMPLATE (type));
@@ -2675,7 +2797,7 @@ pushtag (name, type, globalize)
 	  if (!context)
 	    context = current_namespace;
 
-	  if ((b->pseudo_global && b->level_chain->parm_flag == 2)
+	  if ((b->template_parms_p && b->level_chain->parm_flag == 2)
 	      || b->parm_flag == 2)
 	    in_class = 1;
 
@@ -4195,29 +4317,6 @@ maybe_push_decl (decl)
     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.  */
 
@@ -4969,9 +5068,9 @@ lookup_tag (form, name, binding_level, thislevel_only)
      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;
+  /* 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)
     {
@@ -4990,11 +5089,11 @@ lookup_tag (form, name, binding_level, thislevel_only)
 	  {
 	    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
+	    /* 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_pseudo_global
+	    if (thislevel_only && !allow_template_parms_p
 		&& old && BINDING_VALUE (old)
 		&& DECL_CLASS_TEMPLATE_P (BINDING_VALUE (old)))
 	      old = TREE_TYPE (BINDING_VALUE (old));
@@ -5037,7 +5136,7 @@ lookup_tag (form, name, binding_level, thislevel_only)
 	  }
       if (thislevel_only && ! level->tag_transparent)
 	{
-	  if (level->pseudo_global && allow_pseudo_global)
+	  if (level->template_parms_p && allow_template_parms_p)
 	    {
 	      /* We must deal with cases like this:
 
@@ -5050,7 +5149,7 @@ lookup_tag (form, name, binding_level, thislevel_only)
 		 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;
+	      allow_template_parms_p = 0;
 	      continue;
 	    }
 	  else
@@ -8680,98 +8779,77 @@ grokfndecl (ctype, type, declarator, orig_declarator, virtualp, flags, quals,
     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);
+    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 (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
+  if (ctype != NULL_TREE
+      && (! TYPE_FOR_JAVA (ctype) || check_java_method (decl))
+      && check)
     {
-      tree tmp;
+      tree old_decl;
 
-      /* 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);
+      old_decl = check_classfn (ctype, decl);
 
-      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 (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 (ctype != NULL_TREE
-	  && (! TYPE_FOR_JAVA (ctype) || check_java_method (decl))
-	  && check)
+      if (old_decl && DECL_STATIC_FUNCTION_P (old_decl)
+	  && TREE_CODE (TREE_TYPE (decl)) == METHOD_TYPE)
 	{
-	  tmp = check_classfn (ctype, decl);
+	  /* 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 (tmp && TREE_CODE (tmp) == TEMPLATE_DECL)
-	    tmp = DECL_TEMPLATE_RESULT (tmp);
+      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);
 
-	  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;
-	    }
+	  /* 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 (ctype == NULL_TREE || check)
-	return decl;
+  if (DECL_CONSTRUCTOR_P (decl) && !grok_ctor_properties (ctype, decl))
+    return NULL_TREE;
 
-      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;
-	}
+  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;
 }
 
diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c
index 851b402..f7ed581 100644
--- a/gcc/cp/decl2.c
+++ b/gcc/cp/decl2.c
@@ -1358,6 +1358,8 @@ check_classfn (ctype, function)
   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.
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index a286f53..9fef5ac 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -551,8 +551,7 @@ begin_template_parm_list ()
 
      pushtag contains special code to call pushdecl_with_scope on the
      TEMPLATE_DECL for S2.  */
-  pushlevel (0);
-  declare_pseudo_global_level ();
+  begin_scope (sk_template_parms);
   ++processing_template_decl;
   ++processing_template_parmlist;
   note_template_header (0);
@@ -596,6 +595,7 @@ check_specialization_scope ()
 void
 begin_specialization ()
 {
+  begin_scope (sk_template_spec);
   note_template_header (1);
   check_specialization_scope ();
 }
@@ -606,6 +606,7 @@ begin_specialization ()
 void 
 end_specialization ()
 {
+  finish_scope ();
   reset_specialization ();
 }
 
@@ -1166,109 +1167,99 @@ check_explicit_specialization (declarator, decl, template_count, flags)
   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;
 
-  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);
+  tsk = current_tmpl_spec_kind (template_count);
 
-	  SET_DECL_TEMPLATE_SPECIALIZATION (decl);
-	  if (ctype)
-	    member_specialization = 1;
-	  else
-	    specialization = 1;
-	}
-      else if (template_header_count == template_count)
+  switch (tsk)
+    {
+    case tsk_none:
+      if (processing_specialization) 
 	{
-	  /* 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 
+      else if (TREE_CODE (declarator) == TEMPLATE_ID_EXPR)
 	{
-	  /* 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();
+	  if (is_friend)
+	    /* This could be something like:
 
-	     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");
-	  
+	       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;
-    }
-  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 <>
+      break;
 
-	 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.  */
+    case tsk_excessive_parms:
+      cp_error ("too many template parameter lists in declaration of `%D'", 
+		decl);
+      return error_mark_node;
 
-	  /* If they said
-	       template <class T> void S<int>::f() {}
-	     that's bogus.  */
+      /* 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 ("template parameters specified in specialization");
+	  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:
 
-      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> struct S { void f(); };
+	       void S<int>::f() {} // Missing template <>
 
-	   template <class T> void f(T);
-	   class S { friend void f<>(int); }  */
-	specialization = 1;
-      else
+	     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>(); */
@@ -1277,6 +1268,22 @@ check_explicit_specialization (declarator, decl, template_count, flags)
 		    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)
@@ -1474,10 +1481,19 @@ check_explicit_specialization (declarator, decl, template_count, flags)
 		  targs = new_targs;
 		}
 		  
-	      decl = instantiate_template (tmpl, targs);
-	      return decl;
+	      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.  */
@@ -1504,7 +1520,7 @@ check_explicit_specialization (declarator, decl, template_count, flags)
 	     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
+	     non-template, name-mangling algorithm will not be used
 	     later.  */
 	  if ((is_member_template (tmpl) || ctype == NULL_TREE)
 	      && name_mangling_version >= 1)
@@ -1863,11 +1879,10 @@ end_template_decl ()
     return;
 
   /* This matches the pushlevel in begin_template_parm_list.  */
-  poplevel (0, 0, 0);
+  finish_scope ();
 
   --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.
@@ -2370,7 +2385,7 @@ push_template_decl_real (decl, is_friend)
     DECL_CONTEXT (decl) = FROB_CONTEXT (current_namespace);
 
   /* See if this is a primary template.  */
-  primary = pseudo_global_level_p ();
+  primary = template_parm_scope_p ();
 
   if (primary)
     {
@@ -2444,9 +2459,6 @@ push_template_decl_real (decl, is_friend)
       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)))
@@ -5597,7 +5609,7 @@ tsubst_decl (t, args, type, in_decl)
 		 template <class T> struct S { 
 		   template <class U> friend void f();
 		 };
-		 template <class U> friend void f() {}
+		 template <class U> void f() {}
 		 template S<int>;
 		 template void f<double>();
 
@@ -7395,7 +7407,7 @@ instantiate_template (tmpl, targ_ptr)
   if (spec != NULL_TREE)
     return spec;
 
-  if (DECL_TEMPLATE_INFO (tmpl))
+  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
@@ -8944,6 +8956,8 @@ most_general_template (decl)
      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.)  */
diff --git a/gcc/testsuite/g++.old-deja/g++.benjamin/tem05.C b/gcc/testsuite/g++.old-deja/g++.benjamin/tem05.C
index 92ad739..fc6af32 100644
--- a/gcc/testsuite/g++.old-deja/g++.benjamin/tem05.C
+++ b/gcc/testsuite/g++.old-deja/g++.benjamin/tem05.C
@@ -49,7 +49,7 @@ unsigned short X_one<T>::ret_id() {
   return id;
 }
 
-export template <class T> template <class T2> // WARNING - 
+export template <class T2> // WARNING - 
 bool compare_ge(T2 test) {
   if (test > type)
     return true;
diff --git a/gcc/testsuite/g++.old-deja/g++.pt/memtemp93.C b/gcc/testsuite/g++.old-deja/g++.pt/memtemp93.C
new file mode 100644
index 0000000..50f7423
--- /dev/null
+++ b/gcc/testsuite/g++.old-deja/g++.pt/memtemp93.C
@@ -0,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; 
+}