re PR c++/66957 (incorrect "is protected within this context" error)
authorJason Merrill <jason@redhat.com>
Thu, 20 Aug 2015 01:45:49 +0000 (21:45 -0400)
committerJason Merrill <jason@gcc.gnu.org>
Thu, 20 Aug 2015 01:45:49 +0000 (21:45 -0400)
PR c++/66957
* search.c (protected_accessible_p): Remove redundant access_in_type.
Add otype parm instead of walking binfo.
(friend_accessible_p): Check SCOPE itself.  Handle class
templates.  Pass through otype.
(dfs_accessible_post): Handle all accessibility cases.
(dfs_accessible_pre): New.
(accessible_p): Use it.  Don't check protected access here.  Pass
decl and otype to dfs_walk.
(member_declared_in_type, dfs_access_in_type_pre): New.
(access_in_type): Use dfs_access_in_type_pre.
* friend.c (add_friend): Fix multiple friends with the same name.

From-SVN: r227023

gcc/cp/ChangeLog
gcc/cp/friend.c
gcc/cp/search.c
gcc/testsuite/g++.dg/inherit/access9.C [new file with mode: 0644]

index 155f86a..2a083c8 100644 (file)
@@ -1,5 +1,18 @@
 2015-08-19  Jason Merrill  <jason@redhat.com>
 
+       PR c++/66957
+       * search.c (protected_accessible_p): Remove redundant access_in_type.
+       Add otype parm instead of walking binfo.
+       (friend_accessible_p): Check SCOPE itself.  Handle class
+       templates.  Pass through otype.
+       (dfs_accessible_post): Handle all accessibility cases.
+       (dfs_accessible_pre): New.
+       (accessible_p): Use it.  Don't check protected access here.  Pass
+       decl and otype to dfs_walk.
+       (member_declared_in_type, dfs_access_in_type_pre): New.
+       (access_in_type): Use dfs_access_in_type_pre.
+       * friend.c (add_friend): Fix multiple friends with the same name.
+
        * lambda.c (current_nonlambda_scope): New.
 
 2015-08-18  Trevor Saunders  <tbsaunde@tbsaunde.org>
index e107cee..f53ce27 100644 (file)
@@ -156,11 +156,9 @@ add_friend (tree type, tree decl, bool complain)
                }
            }
 
-         maybe_add_class_template_decl_list (type, decl, /*friend_p=*/1);
-
          TREE_VALUE (list) = tree_cons (NULL_TREE, decl,
                                         TREE_VALUE (list));
-         return;
+         break;
        }
       list = TREE_CHAIN (list);
     }
@@ -172,9 +170,10 @@ add_friend (tree type, tree decl, bool complain)
 
   maybe_add_class_template_decl_list (type, decl, /*friend_p=*/1);
 
-  DECL_FRIENDLIST (typedecl)
-    = tree_cons (DECL_NAME (decl), build_tree_list (NULL_TREE, decl),
-                DECL_FRIENDLIST (typedecl));
+  if (!list)
+    DECL_FRIENDLIST (typedecl)
+      = tree_cons (DECL_NAME (decl), build_tree_list (NULL_TREE, decl),
+                  DECL_FRIENDLIST (typedecl));
   if (!uses_template_parms (type))
     DECL_BEFRIENDING_CLASSES (decl)
       = tree_cons (NULL_TREE, type,
index 90cd243..42db122 100644 (file)
@@ -58,8 +58,6 @@ static tree dfs_walk_once_accessible (tree, bool,
                                      void *data);
 static tree dfs_access_in_type (tree, void *);
 static access_kind access_in_type (tree, tree);
-static int protected_accessible_p (tree, tree, tree);
-static int friend_accessible_p (tree, tree, tree);
 static tree dfs_get_pure_virtuals (tree, void *);
 
 \f
@@ -582,9 +580,36 @@ context_for_name_lookup (tree decl)
   return context;
 }
 
+/* Returns true iff DECL is declared in TYPE.  */
+
+static bool
+member_declared_in_type (tree decl, tree type)
+{
+  /* A normal declaration obviously counts.  */
+  if (context_for_name_lookup (decl) == type)
+    return true;
+  /* So does a using or access declaration.  */
+  if (DECL_LANG_SPECIFIC (decl) && !DECL_DISCRIMINATOR_P (decl)
+      && purpose_member (type, DECL_ACCESS (decl)))
+    return true;
+  return false;
+}
+
 /* The accessibility routines use BINFO_ACCESS for scratch space
    during the computation of the accessibility of some declaration.  */
 
+/* Avoid walking up past a declaration of the member.  */
+
+static tree
+dfs_access_in_type_pre (tree binfo, void *data)
+{
+  tree decl = (tree) data;
+  tree type = BINFO_TYPE (binfo);
+  if (member_declared_in_type (decl, type))
+    return dfs_skip_bases;
+  return NULL_TREE;
+}
+
 #define BINFO_ACCESS(NODE) \
   ((access_kind) ((TREE_PUBLIC (NODE) << 1) | TREE_PRIVATE (NODE)))
 
@@ -705,19 +730,17 @@ access_in_type (tree type, tree decl)
     The algorithm we use is to make a post-order depth-first traversal
     of the base-class hierarchy.  As we come up the tree, we annotate
     each node with the most lenient access.  */
-  dfs_walk_once (binfo, NULL, dfs_access_in_type, decl);
+  dfs_walk_once (binfo, dfs_access_in_type_pre, dfs_access_in_type, decl);
 
   return BINFO_ACCESS (binfo);
 }
 
-/* Returns nonzero if it is OK to access DECL through an object
-   indicated by BINFO in the context of DERIVED.  */
+/* Returns nonzero if it is OK to access DECL named in TYPE through an object
+   of OTYPE in the context of DERIVED.  */
 
 static int
-protected_accessible_p (tree decl, tree derived, tree binfo)
+protected_accessible_p (tree decl, tree derived, tree type, tree otype)
 {
-  access_kind access;
-
   /* We're checking this clause from [class.access.base]
 
        m as a member of N is protected, and the reference occurs in a
@@ -725,16 +748,10 @@ protected_accessible_p (tree decl, tree derived, tree binfo)
        class P derived from N, where m as a member of P is public, private
        or protected.
 
-    Here DERIVED is a possible P, DECL is m and BINFO_TYPE (binfo) is N.  */
+    Here DERIVED is a possible P, DECL is m and TYPE is N.  */
 
   /* If DERIVED isn't derived from N, then it can't be a P.  */
-  if (!DERIVED_FROM_P (BINFO_TYPE (binfo), derived))
-    return 0;
-
-  access = access_in_type (derived, decl);
-
-  /* If m is inaccessible in DERIVED, then it's not a P.  */
-  if (access == ak_none)
+  if (!DERIVED_FROM_P (type, derived))
     return 0;
 
   /* [class.protected]
@@ -748,33 +765,38 @@ protected_accessible_p (tree decl, tree derived, tree binfo)
      derived from that class) (_expr.ref_).  If the access is to form
      a pointer to member, the nested-name-specifier shall name the
      derived class (or any class derived from that class).  */
-  if (DECL_NONSTATIC_MEMBER_P (decl))
-    {
-      /* We can tell through what the reference is occurring by
-        chasing BINFO up to the root.  */
-      tree t = binfo;
-      while (BINFO_INHERITANCE_CHAIN (t))
-       t = BINFO_INHERITANCE_CHAIN (t);
-
-      if (!DERIVED_FROM_P (derived, BINFO_TYPE (t)))
-       return 0;
-    }
+  if (DECL_NONSTATIC_MEMBER_P (decl)
+      && !DERIVED_FROM_P (derived, otype))
+    return 0;
 
   return 1;
 }
 
-/* Returns nonzero if SCOPE is a friend of a type which would be able
-   to access DECL through the object indicated by BINFO.  */
+/* Returns nonzero if SCOPE is a type or a friend of a type which would be able
+   to access DECL through TYPE.  OTYPE is the type of the object.  */
 
 static int
-friend_accessible_p (tree scope, tree decl, tree binfo)
+friend_accessible_p (tree scope, tree decl, tree type, tree otype)
 {
+  /* We're checking this clause from [class.access.base]
+
+       m as a member of N is protected, and the reference occurs in a
+       member or friend of class N, or in a member or friend of a
+       class P derived from N, where m as a member of P is public, private
+       or protected.
+
+    Here DECL is m and TYPE is N.  SCOPE is the current context,
+    and we check all its possible Ps.  */
   tree befriending_classes;
   tree t;
 
   if (!scope)
     return 0;
 
+  /* Is SCOPE itself a suitable P?  */
+  if (TYPE_P (scope) && protected_accessible_p (decl, scope, type, otype))
+    return 1;
+
   if (DECL_DECLARES_FUNCTION_P (scope))
     befriending_classes = DECL_BEFRIENDING_CLASSES (scope);
   else if (TYPE_P (scope))
@@ -783,54 +805,113 @@ friend_accessible_p (tree scope, tree decl, tree binfo)
     return 0;
 
   for (t = befriending_classes; t; t = TREE_CHAIN (t))
-    if (protected_accessible_p (decl, TREE_VALUE (t), binfo))
+    if (protected_accessible_p (decl, TREE_VALUE (t), type, otype))
       return 1;
 
   /* Nested classes have the same access as their enclosing types, as
-     per DR 45 (this is a change from the standard).  */
+     per DR 45 (this is a change from C++98).  */
   if (TYPE_P (scope))
-    for (t = TYPE_CONTEXT (scope); t && TYPE_P (t); t = TYPE_CONTEXT (t))
-      if (protected_accessible_p (decl, t, binfo))
-       return 1;
+    if (friend_accessible_p (TYPE_CONTEXT (scope), decl, type, otype))
+      return 1;
 
   if (DECL_DECLARES_FUNCTION_P (scope))
     {
       /* Perhaps this SCOPE is a member of a class which is a
         friend.  */
       if (DECL_CLASS_SCOPE_P (scope)
-         && friend_accessible_p (DECL_CONTEXT (scope), decl, binfo))
+         && friend_accessible_p (DECL_CONTEXT (scope), decl, type, otype))
        return 1;
+    }
 
-      /* Or an instantiation of something which is a friend.  */
-      if (DECL_TEMPLATE_INFO (scope))
+  /* Maybe scope's template is a friend.  */
+  if (tree tinfo = get_template_info (scope))
+    {
+      tree tmpl = TI_TEMPLATE (tinfo);
+      if (DECL_CLASS_TEMPLATE_P (tmpl))
+       tmpl = TREE_TYPE (tmpl);
+      else
+       tmpl = DECL_TEMPLATE_RESULT (tmpl);
+      if (tmpl != scope)
        {
-         int ret;
          /* Increment processing_template_decl to make sure that
             dependent_type_p works correctly.  */
          ++processing_template_decl;
-         ret = friend_accessible_p (DECL_TI_TEMPLATE (scope), decl, binfo);
+         int ret = friend_accessible_p (tmpl, decl, type, otype);
          --processing_template_decl;
-         return ret;
+         if (ret)
+           return 1;
        }
     }
 
+  /* If is_friend is true, we should have found a befriending class.  */
+  gcc_checking_assert (!is_friend (type, scope));
+
   return 0;
 }
 
+struct dfs_accessible_data
+{
+  tree decl;
+  tree object_type;
+};
+
+/* Avoid walking up past a declaration of the member.  */
+
+static tree
+dfs_accessible_pre (tree binfo, void *data)
+{
+  dfs_accessible_data *d = (dfs_accessible_data *)data;
+  tree type = BINFO_TYPE (binfo);
+  if (member_declared_in_type (d->decl, type))
+    return dfs_skip_bases;
+  return NULL_TREE;
+}
+
 /* Called via dfs_walk_once_accessible from accessible_p */
 
 static tree
-dfs_accessible_post (tree binfo, void * /*data*/)
+dfs_accessible_post (tree binfo, void *data)
 {
-  if (BINFO_ACCESS (binfo) != ak_none)
+  /* access_in_type already set BINFO_ACCESS for us.  */
+  access_kind access = BINFO_ACCESS (binfo);
+  tree N = BINFO_TYPE (binfo);
+  dfs_accessible_data *d = (dfs_accessible_data *)data;
+  tree decl = d->decl;
+  tree scope = current_nonlambda_scope ();
+
+  /* A member m is accessible at the point R when named in class N if */
+  switch (access)
     {
-      tree scope = current_scope ();
-      if (scope && TREE_CODE (scope) != NAMESPACE_DECL
-         && is_friend (BINFO_TYPE (binfo), scope))
-       return binfo;
-    }
+    case ak_none:
+      return NULL_TREE;
 
-  return NULL_TREE;
+    case ak_public:
+      /* m as a member of N is public, or */
+      return binfo;
+
+    case ak_private:
+      {
+       /* m as a member of N is private, and R occurs in a member or friend of
+          class N, or */
+       if (scope && TREE_CODE (scope) != NAMESPACE_DECL
+           && is_friend (N, scope))
+         return binfo;
+       return NULL_TREE;
+      }
+
+    case ak_protected:
+      {
+       /* m as a member of N is protected, and R occurs in a member or friend
+          of class N, or in a member or friend of a class P derived from N,
+          where m as a member of P is public, private, or protected  */
+       if (friend_accessible_p (scope, decl, N, d->object_type))
+         return binfo;
+       return NULL_TREE;
+      }
+
+    default:
+      gcc_unreachable ();
+    }
 }
 
 /* Like accessible_p below, but within a template returns true iff DECL is
@@ -858,21 +939,15 @@ int
 accessible_p (tree type, tree decl, bool consider_local_p)
 {
   tree binfo;
-  tree scope;
   access_kind access;
 
-  /* Nonzero if it's OK to access DECL if it has protected
-     accessibility in TYPE.  */
-  int protected_ok = 0;
-
   /* If this declaration is in a block or namespace scope, there's no
      access control.  */
   if (!TYPE_P (context_for_name_lookup (decl)))
     return 1;
 
   /* There is no need to perform access checks inside a thunk.  */
-  scope = current_scope ();
-  if (scope && DECL_THUNK_P (scope))
+  if (current_function_decl && DECL_THUNK_P (current_function_decl))
     return 1;
 
   /* In a template declaration, we cannot be sure whether the
@@ -886,13 +961,18 @@ accessible_p (tree type, tree decl, bool consider_local_p)
       && (!processing_template_parmlist || processing_template_decl > 1))
     return 1;
 
+  tree otype;
   if (!TYPE_P (type))
     {
-      binfo = type;
+      /* When accessing a non-static member, the most derived type in the
+        binfo chain is the type of the object; remember that type for
+        protected_accessible_p.  */
+      for (tree b = type; b; b = BINFO_INHERITANCE_CHAIN (b))
+       otype = BINFO_TYPE (b);
       type = BINFO_TYPE (type);
     }
   else
-    binfo = TYPE_BINFO (type);
+    otype = type;
 
   /* [class.access.base]
 
@@ -905,7 +985,7 @@ accessible_p (tree type, tree decl, bool consider_local_p)
 
      --m as a member of N is protected, and the reference occurs in a
        member or friend of class N, or in a member or friend of a
-       class P derived from N, where m as a member of P is private or
+       class P derived from N, where m as a member of P is public, private or
        protected, or
 
      --there exists a base class B of N that is accessible at the point
@@ -913,40 +993,28 @@ accessible_p (tree type, tree decl, bool consider_local_p)
 
     We walk the base class hierarchy, checking these conditions.  */
 
-  if (consider_local_p)
-    {
-      /* Figure out where the reference is occurring.  Check to see if
-        DECL is private or protected in this scope, since that will
-        determine whether protected access is allowed.  */
-      tree ct = current_nonlambda_class_type ();
-      if (ct)
-       protected_ok = protected_accessible_p (decl,
-                                              ct,
-                                              binfo);
-
-      /* Now, loop through the classes of which we are a friend.  */
-      if (!protected_ok)
-       protected_ok = friend_accessible_p (scope, decl, binfo);
-    }
-
-  /* Standardize the binfo that access_in_type will use.  We don't
-     need to know what path was chosen from this point onwards.  */
+  /* We walk using TYPE_BINFO (type) because access_in_type will set
+     BINFO_ACCESS on it and its bases.  */
   binfo = TYPE_BINFO (type);
 
   /* Compute the accessibility of DECL in the class hierarchy
      dominated by type.  */
   access = access_in_type (type, decl);
-  if (access == ak_public
-      || (access == ak_protected && protected_ok))
+  if (access == ak_public)
     return 1;
 
+  /* If we aren't considering the point of reference, only the first bullet
+     applies.  */
   if (!consider_local_p)
     return 0;
 
+  dfs_accessible_data d = { decl, otype };
+
   /* Walk the hierarchy again, looking for a base class that allows
      access.  */
   return dfs_walk_once_accessible (binfo, /*friends=*/true,
-                                  NULL, dfs_accessible_post, NULL)
+                                  dfs_accessible_pre,
+                                  dfs_accessible_post, &d)
     != NULL_TREE;
 }
 
diff --git a/gcc/testsuite/g++.dg/inherit/access9.C b/gcc/testsuite/g++.dg/inherit/access9.C
new file mode 100644 (file)
index 0000000..cdbc640
--- /dev/null
@@ -0,0 +1,14 @@
+// PR c++/66957
+
+class BaseClass {
+protected:
+  static int x;
+};
+
+struct DerivedA : BaseClass { };
+
+struct DerivedB : BaseClass {
+  DerivedB() {
+    (void) DerivedA::x;
+  }
+};