Patchwork C++ PATCH for c++/48884 (access control and deduction)

login
register
mail settings
Submitter Jason Merrill
Date May 24, 2011, 8:24 p.m.
Message ID <4DDC13F4.5010409@redhat.com>
Download mbox | patch
Permalink /patch/97220/
State New
Headers show

Comments

Jason Merrill - May 24, 2011, 8:24 p.m.
In this PR, we had a problem with hitting access control violations 
during the substitution of template arguments in the context of template 
argument deduction.  If we're going to be doing access checking at all 
during deduction substitution, we need to do it in the context of the 
function we're doing deduction for.  In C++98 mode we just disable 
access checking.

Tested x86_64-pc-linux-gnu, applying to trunk.

Patch

commit ffb9b449d208ef324c092df35460d9e0b34eacfd
Author: Jason Merrill <jason@redhat.com>
Date:   Mon May 23 16:02:45 2011 -0400

    	PR c++/48884
    	* class.c (pushclass): Accept NULL argument.
    	(popclass): Deal with popping null class.
    	* pt.c (push_access_scope, pop_access_scope): Use them rather than
    	push_to_top_level/pop_from_top_level.
    	(push_deduction_access_scope, pop_defarg_context): New.
    	(fn_type_unification): Use them.
    	* name-lookup.c (lookup_name_real_1): Check current_class_type.

diff --git a/gcc/cp/class.c b/gcc/cp/class.c
index 4e52b18..69627cb 100644
--- a/gcc/cp/class.c
+++ b/gcc/cp/class.c
@@ -6082,6 +6082,9 @@  restore_class_cache (void)
    So that we may avoid calls to lookup_name, we cache the _TYPE
    nodes of local TYPE_DECLs in the TREE_TYPE field of the name.
 
+   For use by push_access_scope, we allow TYPE to be null to temporarily
+   push out of class scope.  This does not actually change binding levels.
+
    For multiple inheritance, we perform a two-pass depth-first search
    of the type lattice.  */
 
@@ -6090,8 +6093,6 @@  pushclass (tree type)
 {
   class_stack_node_t csn;
 
-  type = TYPE_MAIN_VARIANT (type);
-
   /* Make sure there is enough room for the new entry on the stack.  */
   if (current_class_depth + 1 >= current_class_stack_size)
     {
@@ -6110,6 +6111,15 @@  pushclass (tree type)
   csn->hidden = 0;
   current_class_depth++;
 
+  if (type == NULL_TREE)
+    {
+      current_class_name = current_class_type = NULL_TREE;
+      csn->hidden = true;
+      return;
+    }
+
+  type = TYPE_MAIN_VARIANT (type);
+
   /* Now set up the new type.  */
   current_class_name = TYPE_NAME (type);
   if (TREE_CODE (current_class_name) == TYPE_DECL)
@@ -6154,7 +6164,11 @@  invalidate_class_lookup_cache (void)
 void
 popclass (void)
 {
-  poplevel_class ();
+  if (current_class_type)
+    poplevel_class ();
+  else
+    gcc_assert (current_class_depth
+		&& current_class_stack[current_class_depth - 1].hidden);
 
   current_class_depth--;
   current_class_name = current_class_stack[current_class_depth].name;
diff --git a/gcc/cp/name-lookup.c b/gcc/cp/name-lookup.c
index 3d1c64d..bb6d4b9 100644
--- a/gcc/cp/name-lookup.c
+++ b/gcc/cp/name-lookup.c
@@ -4469,7 +4469,7 @@  lookup_name_real_1 (tree name, int prefer_type, int nonclass, bool block_p,
   /* Conversion operators are handled specially because ordinary
      unqualified name lookup will not find template conversion
      operators.  */
-  if (IDENTIFIER_TYPENAME_P (name))
+  if (IDENTIFIER_TYPENAME_P (name) && current_class_type)
     {
       struct cp_binding_level *level;
 
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index dbff91e..98844c3 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -212,7 +212,7 @@  push_access_scope (tree t)
   else if (DECL_CLASS_SCOPE_P (t))
     push_nested_class (DECL_CONTEXT (t));
   else
-    push_to_top_level ();
+    pushclass (NULL_TREE);
 
   if (TREE_CODE (t) == FUNCTION_DECL)
     {
@@ -237,7 +237,7 @@  pop_access_scope (tree t)
   if (DECL_FRIEND_CONTEXT (t) || DECL_CLASS_SCOPE_P (t))
     pop_nested_class ();
   else
-    pop_from_top_level ();
+    popclass ();
 }
 
 /* Do any processing required when DECL (a member template
@@ -13820,6 +13820,30 @@  instantiate_template (tree tmpl, tree orig_args, tsubst_flags_t complain)
   return ret;
 }
 
+/* We're going to do deduction substitution on the type of TMPL, a function
+   template.  In C++11 mode, push into that access scope.  In C++03 mode,
+   disable access checking.  */
+
+static void
+push_deduction_access_scope (tree tmpl)
+{
+  if (cxx_dialect >= cxx0x)
+    push_access_scope (DECL_TEMPLATE_RESULT (tmpl));
+  else
+    push_deferring_access_checks (dk_no_check);
+}
+
+/* And pop back out.  */
+
+static void
+pop_deduction_access_scope (tree tmpl)
+{
+  if (cxx_dialect >= cxx0x)
+    pop_access_scope (DECL_TEMPLATE_RESULT (tmpl));
+  else
+    pop_deferring_access_checks ();
+}
+
 /* The FN is a TEMPLATE_DECL for a function.  ARGS is an array with
    NARGS elements of the arguments that are being used when calling
    it.  TARGS is a vector into which the deduced template arguments
@@ -13958,7 +13982,9 @@  fn_type_unification (tree fn,
         incomplete = NUM_TMPL_ARGS (explicit_targs) != NUM_TMPL_ARGS (targs);
 
       processing_template_decl += incomplete;
+      push_deduction_access_scope (fn);
       fntype = deduction_tsubst_fntype (fn, converted_args);
+      pop_deduction_access_scope (fn);
       processing_template_decl -= incomplete;
 
       if (fntype == error_mark_node)
@@ -14029,7 +14055,10 @@  fn_type_unification (tree fn,
        substitution results in an invalid type, as described above,
        type deduction fails.  */
     {
-      tree substed = deduction_tsubst_fntype (fn, targs);
+      tree substed;
+      push_deduction_access_scope (fn);
+      substed = deduction_tsubst_fntype (fn, targs);
+      pop_deduction_access_scope (fn);
       if (substed == error_mark_node)
 	return 1;
 
diff --git a/gcc/testsuite/g++.dg/template/access21.C b/gcc/testsuite/g++.dg/template/access21.C
new file mode 100644
index 0000000..8414c43
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/access21.C
@@ -0,0 +1,23 @@ 
+// PR c++/48884
+
+class X
+{
+  static const int I = 42;
+  friend struct Y;
+};
+
+template <int I> struct A { };
+
+struct Y
+{
+  template <typename T>
+  static A<T::I> f(T t)
+  {
+    return A<T::I>();
+  }
+};
+
+int main()
+{
+  Y::f(X());
+}
diff --git a/gcc/testsuite/g++.dg/template/access22.C b/gcc/testsuite/g++.dg/template/access22.C
new file mode 100644
index 0000000..9ee28a2
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/access22.C
@@ -0,0 +1,15 @@ 
+template <int I> struct B { };
+
+template <class T>
+B<T::I> f();
+
+class A
+{
+  static const int I = 42;
+  template <class T> friend B<T::I> f();
+};
+
+int main()
+{
+  f<A>();
+}