From 7a468236d23ceb6fa13337aae68e8790dac61cfe Mon Sep 17 00:00:00 2001
From: lerdsuwa <lerdsuwa@138bc75d-0d04-0410-961f-82ee72b054a4>
Date: Wed, 8 Jan 2003 14:42:39 +0000
Subject: [PATCH] 	PR c++/9030 	* decl.c (make_typename_type): Check
 access only when tf_error. 	(make_unbound_class_template): Likewise. 
 * pt.c (saved_access_scope): New variable. 	(push_access_scope_real): New
 function. 	(push_access_scope): Likewise. 	(pop_access_scope): Likewise. 
 (tsubst_default_argument): Use them. 	(instantiate_template): Likewise. 
 (regenerate_decl_from_template): Likewise. 	(instantiate_decl): Likewise. 
 (get_mostly_instantiated_function_type): Likewise.

	* g++.dg/template/friend12.C: New test.
	* g++.dg/template/friend13.C: Likewise.
	* g++.old-deja/g++.eh/spec6.C: Add missing error message.


git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@61046 138bc75d-0d04-0410-961f-82ee72b054a4
---
 gcc/cp/ChangeLog                          |  15 +++
 gcc/cp/decl.c                             |  33 ++++--
 gcc/cp/pt.c                               | 127 +++++++++++++++++-----
 gcc/testsuite/ChangeLog                   |   7 ++
 gcc/testsuite/g++.dg/template/friend12.C  |  24 ++++
 gcc/testsuite/g++.dg/template/friend13.C  |  21 ++++
 gcc/testsuite/g++.old-deja/g++.eh/spec6.C |   2 +-
 7 files changed, 187 insertions(+), 42 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/template/friend12.C
 create mode 100644 gcc/testsuite/g++.dg/template/friend13.C

diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index 9b7f67a783e5..26207b7a3736 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -1,3 +1,18 @@
+2003-01-08  Kriang Lerdsuwanakij  <lerdsuwa@users.sourceforge.net>
+
+	PR c++/9030
+	* decl.c (make_typename_type): Check access only when tf_error.
+	(make_unbound_class_template): Likewise.
+	* pt.c (saved_access_scope): New variable.
+	(push_access_scope_real): New function.
+	(push_access_scope): Likewise.
+	(pop_access_scope): Likewise.
+	(tsubst_default_argument): Use them.
+	(instantiate_template): Likewise.
+	(regenerate_decl_from_template): Likewise.
+	(instantiate_decl): Likewise.
+	(get_mostly_instantiated_function_type): Likewise.
+
 2003-01-07  Nathanael Nerode <neroden@gcc.gnu.org>
 
 	* tree.c: Delete bogus #if 0 code.
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index f8141823e30c..538519ed0437 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -5671,10 +5671,13 @@ make_typename_type (context, name, complain)
 	      return error_mark_node;
 	    }
 
-	  if (complain & tf_parsing)
-	    type_access_control (context, tmpl);
-	  else
-	    enforce_access (context, tmpl);
+	  if (complain & tf_error)
+	    {
+	      if (complain & tf_parsing)
+		type_access_control (context, tmpl);
+	      else
+		enforce_access (context, tmpl);
+	    }
 
 	  return lookup_template_class (tmpl,
 					TREE_OPERAND (fullname, 1),
@@ -5703,10 +5706,13 @@ make_typename_type (context, name, complain)
 		  return error_mark_node;
 		}
 
-	      if (complain & tf_parsing)
-		type_access_control (context, t);
-	      else
-		enforce_access (context, t);
+	      if (complain & tf_error)
+		{
+	      	  if (complain & tf_parsing)
+		    type_access_control (context, t);
+		  else
+		    enforce_access (context, t);
+		}
 
 	      if (DECL_ARTIFICIAL (t) || !(complain & tf_keep_type_decl))
 		t = TREE_TYPE (t);
@@ -5774,10 +5780,13 @@ make_unbound_class_template (context, name, complain)
 	  return error_mark_node;
 	}
       
-      if (complain & tf_parsing)
-	type_access_control (context, tmpl);
-      else
-	enforce_access (context, tmpl);
+      if (complain & tf_error)
+	{
+	  if (complain & tf_parsing)
+	    type_access_control (context, tmpl);
+	  else
+	    enforce_access (context, tmpl);
+	}
 
       return tmpl;
     }
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index f7a9114d7a81..b63443b174bb 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -67,6 +67,8 @@ static size_t inline_parm_levels_used;
 
 static GTY(()) tree current_tinst_level;
 
+static GTY(()) tree saved_access_scope;
+
 /* A map from local variable declarations in the body of the template
    presently being instantiated to the corresponding instantiated
    local variables.  */
@@ -88,6 +90,9 @@ static htab_t local_specializations;
 #define GTB_IGNORE_TYPE 2 /* We don't need to try to unify the current
 			     type with the desired type.  */
 
+static void push_access_scope_real PARAMS ((tree, tree, tree));
+static void push_access_scope PARAMS ((tree));
+static void pop_access_scope PARAMS ((tree));
 static int resolve_overloaded_unification PARAMS ((tree, tree, tree, tree,
 						   unification_kind_t, int));
 static int try_one_overload PARAMS ((tree, tree, tree, tree, tree,
@@ -168,6 +173,80 @@ static int invalid_nontype_parm_type_p PARAMS ((tree, tsubst_flags_t));
 static int eq_local_specializations (const void *, const void *);
 static tree template_for_substitution (tree);
 
+/* Make the current scope suitable for access checking when we are
+   processing T.  T can be FUNCTION_DECL for instantiated function
+   template, TEMPLATE_DECL for uninstantiated one, or VAR_DECL for
+   static member variable (need by instantiate_decl).  ARGS is the 
+   template argument for TEMPLATE_DECL.  If CONTEXT is not NULL_TREE, 
+   this is used instead of the context of T.  */
+
+void
+push_access_scope_real (t, args, context)
+  tree t, args, context;
+{
+  if (TREE_CODE (t) == FUNCTION_DECL || DECL_FUNCTION_TEMPLATE_P (t))
+    {
+      /* When we are processing specialization `foo<Outer>' for code like
+
+	   template <class U> typename U::Inner foo ();
+	   class Outer {
+	     struct Inner {};
+	     friend Outer::Inner foo<Outer> ();
+	   };
+
+	 `T' is a TEMPLATE_DECL, but `Outer' is only a friend of one of
+	 its specialization.  We can get the FUNCTION_DECL with the right
+	 information because this specialization has already been
+	 registered by the friend declaration above.  */
+
+      if (DECL_FUNCTION_TEMPLATE_P (t) && args)
+	{
+	  tree full_args = tsubst_template_arg_vector
+	    (DECL_TI_ARGS (DECL_TEMPLATE_RESULT (t)), args, tf_none);
+	  tree spec = NULL_TREE;
+	  if (full_args != error_mark_node)
+	    spec = retrieve_specialization (t, full_args);
+	  if (spec)
+	    t = spec;
+	}
+
+      saved_access_scope = tree_cons
+	(NULL_TREE, current_function_decl, saved_access_scope);
+      current_function_decl = t;
+    }
+
+  if (!context)
+    context = DECL_CONTEXT (t);
+  if (context && TYPE_P (context))
+    push_nested_class (context, 2);
+}
+
+/* Like push_access_scope_real, but always uses DECL_CONTEXT.  */
+
+void
+push_access_scope (t)
+  tree t;
+{
+  push_access_scope_real (t, NULL_TREE, NULL_TREE);
+}
+
+/* Restore the scope set up by push_access_scope.  T is the node we
+   are processing.  */
+
+void
+pop_access_scope (t)
+  tree t;
+{
+  if (DECL_CLASS_SCOPE_P (t))
+    pop_nested_class ();
+
+  if (TREE_CODE (t) == FUNCTION_DECL || DECL_FUNCTION_TEMPLATE_P (t))
+    {
+      current_function_decl = TREE_VALUE (saved_access_scope);
+      saved_access_scope = TREE_CHAIN (saved_access_scope);
+    }
+}
+
 /* Do any processing required when DECL (a member template
    declaration) is finished.  Returns the TEMPLATE_DECL corresponding
    to DECL, unless it is a specialization, in which case the DECL
@@ -5733,14 +5812,14 @@ tsubst_default_argument (fn, type, arg)
      ??? current_class_type affects a lot more than name lookup.  This is
      very fragile.  Fortunately, it will go away when we do 2-phase name
      binding properly.  */
-  if (DECL_CLASS_SCOPE_P (fn))
-    pushclass (DECL_CONTEXT (fn), 2);
+
+  /* FN is already the desired FUNCTION_DECL.  */
+  push_access_scope (fn);
 
   arg = tsubst_expr (arg, DECL_TI_ARGS (fn),
 		     tf_error | tf_warning, NULL_TREE);
   
-  if (DECL_CLASS_SCOPE_P (fn))
-    popclass ();
+  pop_access_scope (fn);
 
   /* Make sure the default argument is reasonable.  */
   arg = check_default_argument (type, arg);
@@ -7873,17 +7952,17 @@ instantiate_template (tmpl, targ_ptr)
     }
 
   /* Make sure that we can see identifiers, and compute access
-     correctly.  */
-  if (DECL_CLASS_SCOPE_P (gen_tmpl))
-    pushclass (tsubst (DECL_CONTEXT (gen_tmpl), targ_ptr, tf_error,
-		       gen_tmpl), 1);
+     correctly.  The desired FUNCTION_DECL for FNDECL may or may not be
+     created earlier.  Let push_access_scope_real figure that out.  */
+  push_access_scope_real
+    (gen_tmpl, targ_ptr, tsubst (DECL_CONTEXT (gen_tmpl), targ_ptr, 
+				 tf_error, gen_tmpl));
 
   /* substitute template parameters */
   fndecl = tsubst (DECL_TEMPLATE_RESULT (gen_tmpl),
 		   targ_ptr, tf_error, gen_tmpl);
 
-  if (DECL_CLASS_SCOPE_P (gen_tmpl))
-    popclass ();
+  pop_access_scope (gen_tmpl);
 
   /* The DECL_TI_TEMPLATE should always be the immediate parent
      template, not the most general template.  */
@@ -7955,7 +8034,7 @@ fn_type_unification (fn, explicit_targs, targs, args, return_type,
   int result;
 
   my_friendly_assert (TREE_CODE (fn) == TEMPLATE_DECL, 0);
-  
+
   fntype = TREE_TYPE (fn);
   if (explicit_targs)
     {
@@ -9977,6 +10056,7 @@ regenerate_decl_from_template (decl, tmpl)
      instantiation of a specialization, which it isn't: it's a full
      instantiation.  */
   gen_tmpl = most_general_template (tmpl);
+  push_access_scope_real (gen_tmpl, args, DECL_CONTEXT (decl));
   unregistered = unregister_specialization (decl, gen_tmpl);
 
   /* If the DECL was not unregistered then something peculiar is
@@ -9984,12 +10064,6 @@ regenerate_decl_from_template (decl, tmpl)
      register_specialization for it.  */
   my_friendly_assert (unregistered, 0);
 
-  if (DECL_CLASS_SCOPE_P (decl))
-    /* Make sure that we can see identifiers, and compute access
-       correctly, for the class members used in the declaration of
-       this static variable or function.  */
-    push_nested_class (DECL_CONTEXT (decl), 2);
-
   /* Do the substitution to get the new declaration.  */
   new_decl = tsubst (code_pattern, args, tf_error, NULL_TREE);
 
@@ -10010,9 +10084,7 @@ regenerate_decl_from_template (decl, tmpl)
       DECL_INITIAL (decl) = NULL_TREE;
     }
 
-  /* Pop the class context we pushed above.  */
-  if (DECL_CLASS_SCOPE_P (decl))
-    pop_nested_class ();
+  pop_access_scope (decl);
 
   /* The immediate parent of the new template is still whatever it was
      before, even though tsubst sets DECL_TI_TEMPLATE up as the most
@@ -10218,9 +10290,9 @@ instantiate_decl (d, defer_ok)
       tree type = TREE_TYPE (gen);
 
       /* Make sure that we can see identifiers, and compute access
-	 correctly.  */
-      if (DECL_CLASS_SCOPE_P (d))
-	pushclass (DECL_CONTEXT (d), 1);
+	 correctly.  D is already the target FUNCTION_DECL with the
+	 right context.  */
+      push_access_scope (d);
 
       if (TREE_CODE (gen) == FUNCTION_DECL)
 	{
@@ -10235,8 +10307,7 @@ instantiate_decl (d, defer_ok)
 	}
       tsubst (type, gen_args, tf_error | tf_warning, d);
 
-      if (DECL_CLASS_SCOPE_P (d))
-	popclass ();
+      pop_access_scope (d);
     }
   
   if (TREE_CODE (d) == VAR_DECL && DECL_INITIALIZED_IN_CLASS_P (d)
@@ -10597,8 +10668,7 @@ get_mostly_instantiated_function_type (decl)
 	 partial substitution here.  It depends only on outer template
 	 parameters, regardless of whether the innermost level is
 	 specialized or not.  */
-      if (DECL_CLASS_SCOPE_P (decl))
-	pushclass (DECL_CONTEXT (decl), 1);
+      push_access_scope (decl);
 
       /* Now, do the (partial) substitution to figure out the
 	 appropriate function type.  */
@@ -10611,8 +10681,7 @@ get_mostly_instantiated_function_type (decl)
       TREE_VEC_LENGTH (partial_args)--;
       tparms = tsubst_template_parms (tparms, partial_args, tf_error);
 
-      if (DECL_CLASS_SCOPE_P (decl))
-	popclass ();
+      pop_access_scope (decl);
     }
 
   return fn_type;
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index ba32c50072b7..848423d59d3a 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,10 @@
+2003-01-08  Kriang Lerdsuwanakij  <lerdsuwa@users.sourceforge.net>
+
+	PR c++/9030
+	* g++.dg/template/friend12.C: New test.
+	* g++.dg/template/friend13.C: Likewise.
+	* g++.old-deja/g++.eh/spec6.C: Add missing error message.
+
 Wed Jan  8 11:41:47 CET 2003  Jan Hubicka  <jh@suse.cz>
 
 	* gcc.dg/i386-cadd.c: New test.
diff --git a/gcc/testsuite/g++.dg/template/friend12.C b/gcc/testsuite/g++.dg/template/friend12.C
new file mode 100644
index 000000000000..0cd561b5a2a2
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/friend12.C
@@ -0,0 +1,24 @@
+// { dg-do compile }
+
+// Origin: Wolfgang Bangerth <bangerth@ticam.utexas.edu>
+
+// PR 9030.  Perform access checking to parameter and return type of 
+// function template correctly when the template is friend.
+
+template <class T> class Outer {
+  private:
+    struct Inner {};
+
+    template <class T_>
+    friend typename Outer<T_>::Inner foo ();
+};
+
+template <class T>
+typename Outer<T>::Inner
+foo () {
+  return typename Outer<T>::Inner();
+}
+
+void f() {
+  foo<int>();
+}
diff --git a/gcc/testsuite/g++.dg/template/friend13.C b/gcc/testsuite/g++.dg/template/friend13.C
new file mode 100644
index 000000000000..6eebf6b951f4
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/friend13.C
@@ -0,0 +1,21 @@
+// { dg-do compile }
+
+// Perform access checking to parameter and return type of 
+// function template correctly when only specialization is friend.
+
+template <class T>
+typename T::Inner
+foo () {
+  return typename T::Inner();
+}
+
+class Outer {
+  private:
+    struct Inner {};
+
+    friend Outer::Inner foo<Outer> ();
+};
+
+void f() {
+  foo<Outer>();
+}
diff --git a/gcc/testsuite/g++.old-deja/g++.eh/spec6.C b/gcc/testsuite/g++.old-deja/g++.eh/spec6.C
index 1bde4cb92b85..d9d4edf1f48d 100644
--- a/gcc/testsuite/g++.old-deja/g++.eh/spec6.C
+++ b/gcc/testsuite/g++.old-deja/g++.eh/spec6.C
@@ -25,7 +25,7 @@ template<class T> void fnx(T *) throw(T){}  // ERROR - invalid use of void expre
 void fx()
 {
   fnx((int *)0);
-  fnx((void *)0);
+  fnx((void *)0);		// ERROR - instantiated from here
 }
 
 // [except.spec] 2, exception specifiers must be the same set of types (but
-- 
GitLab