re PR c++/66957 (incorrect "is protected within this context" error)

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
This commit is contained in:
Jason Merrill 2015-08-19 21:45:49 -04:00 committed by Jason Merrill
parent 18c4fa8ef0
commit 7ac2c0bd17
4 changed files with 181 additions and 87 deletions

View 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>

View 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,

View 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 *);
@ -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)
{
tree scope = current_scope ();
if (scope && TREE_CODE (scope) != NAMESPACE_DECL
&& is_friend (BINFO_TYPE (binfo), scope))
return binfo;
}
/* 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 ();
return NULL_TREE;
/* A member m is accessible at the point R when named in class N if */
switch (access)
{
case ak_none:
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;
}

View File

@ -0,0 +1,14 @@
// PR c++/66957
class BaseClass {
protected:
static int x;
};
struct DerivedA : BaseClass { };
struct DerivedB : BaseClass {
DerivedB() {
(void) DerivedA::x;
}
};