Commit 7ac2c0bd by Jason Merrill Committed by Jason Merrill

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
parent 18c4fa8e
2015-08-19 Jason Merrill <jason@redhat.com> 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. * lambda.c (current_nonlambda_scope): New.
2015-08-18 Trevor Saunders <tbsaunde@tbsaunde.org> 2015-08-18 Trevor Saunders <tbsaunde@tbsaunde.org>
......
...@@ -156,11 +156,9 @@ add_friend (tree type, tree decl, bool complain) ...@@ -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) = tree_cons (NULL_TREE, decl,
TREE_VALUE (list)); TREE_VALUE (list));
return; break;
} }
list = TREE_CHAIN (list); list = TREE_CHAIN (list);
} }
...@@ -172,9 +170,10 @@ add_friend (tree type, tree decl, bool complain) ...@@ -172,9 +170,10 @@ add_friend (tree type, tree decl, bool complain)
maybe_add_class_template_decl_list (type, decl, /*friend_p=*/1); maybe_add_class_template_decl_list (type, decl, /*friend_p=*/1);
DECL_FRIENDLIST (typedecl) if (!list)
= tree_cons (DECL_NAME (decl), build_tree_list (NULL_TREE, decl), DECL_FRIENDLIST (typedecl)
DECL_FRIENDLIST (typedecl)); = tree_cons (DECL_NAME (decl), build_tree_list (NULL_TREE, decl),
DECL_FRIENDLIST (typedecl));
if (!uses_template_parms (type)) if (!uses_template_parms (type))
DECL_BEFRIENDING_CLASSES (decl) DECL_BEFRIENDING_CLASSES (decl)
= tree_cons (NULL_TREE, type, = tree_cons (NULL_TREE, type,
......
...@@ -58,8 +58,6 @@ static tree dfs_walk_once_accessible (tree, bool, ...@@ -58,8 +58,6 @@ static tree dfs_walk_once_accessible (tree, bool,
void *data); void *data);
static tree dfs_access_in_type (tree, void *); static tree dfs_access_in_type (tree, void *);
static access_kind access_in_type (tree, tree); 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 *); static tree dfs_get_pure_virtuals (tree, void *);
...@@ -582,9 +580,36 @@ context_for_name_lookup (tree decl) ...@@ -582,9 +580,36 @@ context_for_name_lookup (tree decl)
return context; 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 /* The accessibility routines use BINFO_ACCESS for scratch space
during the computation of the accessibility of some declaration. */ 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) \ #define BINFO_ACCESS(NODE) \
((access_kind) ((TREE_PUBLIC (NODE) << 1) | TREE_PRIVATE (NODE))) ((access_kind) ((TREE_PUBLIC (NODE) << 1) | TREE_PRIVATE (NODE)))
...@@ -705,19 +730,17 @@ access_in_type (tree type, tree decl) ...@@ -705,19 +730,17 @@ access_in_type (tree type, tree decl)
The algorithm we use is to make a post-order depth-first traversal 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 of the base-class hierarchy. As we come up the tree, we annotate
each node with the most lenient access. */ 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); return BINFO_ACCESS (binfo);
} }
/* Returns nonzero if it is OK to access DECL through an object /* Returns nonzero if it is OK to access DECL named in TYPE through an object
indicated by BINFO in the context of DERIVED. */ of OTYPE in the context of DERIVED. */
static int 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] /* We're checking this clause from [class.access.base]
m as a member of N is protected, and the reference occurs in a 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) ...@@ -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 class P derived from N, where m as a member of P is public, private
or protected. 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 isn't derived from N, then it can't be a P. */
if (!DERIVED_FROM_P (BINFO_TYPE (binfo), derived)) if (!DERIVED_FROM_P (type, 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)
return 0; return 0;
/* [class.protected] /* [class.protected]
...@@ -748,33 +765,38 @@ protected_accessible_p (tree decl, tree derived, tree binfo) ...@@ -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 derived from that class) (_expr.ref_). If the access is to form
a pointer to member, the nested-name-specifier shall name the a pointer to member, the nested-name-specifier shall name the
derived class (or any class derived from that class). */ derived class (or any class derived from that class). */
if (DECL_NONSTATIC_MEMBER_P (decl)) if (DECL_NONSTATIC_MEMBER_P (decl)
{ && !DERIVED_FROM_P (derived, otype))
/* We can tell through what the reference is occurring by return 0;
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;
}
return 1; return 1;
} }
/* Returns nonzero if SCOPE is a friend of a type which would be able /* Returns nonzero if SCOPE is a type or a friend of a type which would be able
to access DECL through the object indicated by BINFO. */ to access DECL through TYPE. OTYPE is the type of the object. */
static int 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 befriending_classes;
tree t; tree t;
if (!scope) if (!scope)
return 0; 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)) if (DECL_DECLARES_FUNCTION_P (scope))
befriending_classes = DECL_BEFRIENDING_CLASSES (scope); befriending_classes = DECL_BEFRIENDING_CLASSES (scope);
else if (TYPE_P (scope)) else if (TYPE_P (scope))
...@@ -783,54 +805,113 @@ friend_accessible_p (tree scope, tree decl, tree binfo) ...@@ -783,54 +805,113 @@ friend_accessible_p (tree scope, tree decl, tree binfo)
return 0; return 0;
for (t = befriending_classes; t; t = TREE_CHAIN (t)) 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; return 1;
/* Nested classes have the same access as their enclosing types, as /* 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)) if (TYPE_P (scope))
for (t = TYPE_CONTEXT (scope); t && TYPE_P (t); t = TYPE_CONTEXT (t)) if (friend_accessible_p (TYPE_CONTEXT (scope), decl, type, otype))
if (protected_accessible_p (decl, t, binfo)) return 1;
return 1;
if (DECL_DECLARES_FUNCTION_P (scope)) if (DECL_DECLARES_FUNCTION_P (scope))
{ {
/* Perhaps this SCOPE is a member of a class which is a /* Perhaps this SCOPE is a member of a class which is a
friend. */ friend. */
if (DECL_CLASS_SCOPE_P (scope) 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; return 1;
}
/* Or an instantiation of something which is a friend. */ /* Maybe scope's template is a friend. */
if (DECL_TEMPLATE_INFO (scope)) 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 /* Increment processing_template_decl to make sure that
dependent_type_p works correctly. */ dependent_type_p works correctly. */
++processing_template_decl; ++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; --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; 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 */ /* Called via dfs_walk_once_accessible from accessible_p */
static tree 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 (); case ak_none:
if (scope && TREE_CODE (scope) != NAMESPACE_DECL return NULL_TREE;
&& is_friend (BINFO_TYPE (binfo), scope))
return binfo;
}
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 /* Like accessible_p below, but within a template returns true iff DECL is
...@@ -858,21 +939,15 @@ int ...@@ -858,21 +939,15 @@ int
accessible_p (tree type, tree decl, bool consider_local_p) accessible_p (tree type, tree decl, bool consider_local_p)
{ {
tree binfo; tree binfo;
tree scope;
access_kind access; 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 /* If this declaration is in a block or namespace scope, there's no
access control. */ access control. */
if (!TYPE_P (context_for_name_lookup (decl))) if (!TYPE_P (context_for_name_lookup (decl)))
return 1; return 1;
/* There is no need to perform access checks inside a thunk. */ /* There is no need to perform access checks inside a thunk. */
scope = current_scope (); if (current_function_decl && DECL_THUNK_P (current_function_decl))
if (scope && DECL_THUNK_P (scope))
return 1; return 1;
/* In a template declaration, we cannot be sure whether the /* In a template declaration, we cannot be sure whether the
...@@ -886,13 +961,18 @@ accessible_p (tree type, tree decl, bool consider_local_p) ...@@ -886,13 +961,18 @@ accessible_p (tree type, tree decl, bool consider_local_p)
&& (!processing_template_parmlist || processing_template_decl > 1)) && (!processing_template_parmlist || processing_template_decl > 1))
return 1; return 1;
tree otype;
if (!TYPE_P (type)) 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); type = BINFO_TYPE (type);
} }
else else
binfo = TYPE_BINFO (type); otype = type;
/* [class.access.base] /* [class.access.base]
...@@ -905,7 +985,7 @@ accessible_p (tree type, tree decl, bool consider_local_p) ...@@ -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 --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 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 protected, or
--there exists a base class B of N that is accessible at the point --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) ...@@ -913,40 +993,28 @@ accessible_p (tree type, tree decl, bool consider_local_p)
We walk the base class hierarchy, checking these conditions. */ We walk the base class hierarchy, checking these conditions. */
if (consider_local_p) /* We walk using TYPE_BINFO (type) because access_in_type will set
{ BINFO_ACCESS on it and its bases. */
/* 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. */
binfo = TYPE_BINFO (type); binfo = TYPE_BINFO (type);
/* Compute the accessibility of DECL in the class hierarchy /* Compute the accessibility of DECL in the class hierarchy
dominated by type. */ dominated by type. */
access = access_in_type (type, decl); access = access_in_type (type, decl);
if (access == ak_public if (access == ak_public)
|| (access == ak_protected && protected_ok))
return 1; return 1;
/* If we aren't considering the point of reference, only the first bullet
applies. */
if (!consider_local_p) if (!consider_local_p)
return 0; return 0;
dfs_accessible_data d = { decl, otype };
/* Walk the hierarchy again, looking for a base class that allows /* Walk the hierarchy again, looking for a base class that allows
access. */ access. */
return dfs_walk_once_accessible (binfo, /*friends=*/true, return dfs_walk_once_accessible (binfo, /*friends=*/true,
NULL, dfs_accessible_post, NULL) dfs_accessible_pre,
dfs_accessible_post, &d)
!= NULL_TREE; != NULL_TREE;
} }
......
// PR c++/66957
class BaseClass {
protected:
static int x;
};
struct DerivedA : BaseClass { };
struct DerivedB : BaseClass {
DerivedB() {
(void) DerivedA::x;
}
};
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment