From: Jason Merrill Date: Thu, 20 Aug 2015 01:45:49 +0000 (-0400) Subject: re PR c++/66957 (incorrect "is protected within this context" error) X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=7ac2c0bd17900b3c0e078479b46de64563a16c9f;p=gcc.git 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 --- diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 155f86af9ff..2a083c804d2 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,5 +1,18 @@ 2015-08-19 Jason Merrill + 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 diff --git a/gcc/cp/friend.c b/gcc/cp/friend.c index e107ceea494..f53ce27f19d 100644 --- a/gcc/cp/friend.c +++ b/gcc/cp/friend.c @@ -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, diff --git a/gcc/cp/search.c b/gcc/cp/search.c index 90cd2438b36..42db122e98c 100644 --- a/gcc/cp/search.c +++ b/gcc/cp/search.c @@ -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) + /* 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 index 00000000000..cdbc6404393 --- /dev/null +++ b/gcc/testsuite/g++.dg/inherit/access9.C @@ -0,0 +1,14 @@ +// PR c++/66957 + +class BaseClass { +protected: + static int x; +}; + +struct DerivedA : BaseClass { }; + +struct DerivedB : BaseClass { + DerivedB() { + (void) DerivedA::x; + } +};