/// Finds the best path from the naming class to the declaring class,
/// taking friend declarations into account.
///
+/// \param FinalAccess the access of the "final step", or AS_none if
+/// there is no final step.
/// \return null if friendship is dependent
static CXXBasePath *FindBestPath(Sema &S,
const EffectiveContext &EC,
CXXRecordDecl *Derived,
CXXRecordDecl *Base,
+ AccessSpecifier FinalAccess,
CXXBasePaths &Paths) {
// Derive the paths to the desired base.
bool isDerived = Derived->isDerivedFrom(Base, Paths);
CXXBasePath *BestPath = 0;
+ assert(FinalAccess != AS_none && "forbidden access after declaring class");
+
// Derive the friend-modified access along each path.
for (CXXBasePaths::paths_iterator PI = Paths.begin(), PE = Paths.end();
PI != PE; ++PI) {
// Walk through the path backwards.
- AccessSpecifier PathAccess = AS_public;
+ AccessSpecifier PathAccess = FinalAccess;
CXXBasePath::iterator I = PI->end(), E = PI->begin();
while (I != E) {
--I;
+ assert(PathAccess != AS_none);
+
+ // If the declaration is a private member of a base class, there
+ // is no level of friendship in derived classes that can make it
+ // accessible.
+ if (PathAccess == AS_private) {
+ PathAccess = AS_none;
+ break;
+ }
+
AccessSpecifier BaseAccess = I->Base->getAccessSpecifier();
if (BaseAccess != AS_public) {
switch (GetFriendKind(S, EC, I->Class)) {
- case Sema::AR_inaccessible: break;
- case Sema::AR_accessible: BaseAccess = AS_public; break;
- case Sema::AR_dependent: return 0;
+ case Sema::AR_inaccessible:
+ PathAccess = CXXRecordDecl::MergeAccess(BaseAccess, PathAccess);
+ break;
+ case Sema::AR_accessible:
+ PathAccess = AS_public;
+ break;
+ case Sema::AR_dependent:
+ return 0;
case Sema::AR_delayed:
llvm_unreachable("friend resolution is never delayed"); break;
}
}
-
- PathAccess = CXXRecordDecl::MergeAccess(BaseAccess, PathAccess);
}
// Note that we modify the path's Access field to the
}
CXXBasePaths Paths;
- CXXBasePath &Path = *FindBestPath(S, EC, NamingClass, DeclaringClass, Paths);
+ CXXBasePath &Path = *FindBestPath(S, EC, NamingClass, DeclaringClass,
+ AS_public, Paths);
CXXBasePath::iterator I = Path.end(), E = Path.begin();
while (I != E) {
CXXRecordDecl *NamingClass = Entity.getNamingClass();
// Adjust the declaration of the referred entity.
- AccessSpecifier DeclAccess = AS_none;
+ AccessSpecifier DeclAccess = AS_public;
if (Entity.isMemberAccess()) {
NamedDecl *Target = Entity.getTargetDecl();
// Append the declaration's access if applicable.
CXXBasePaths Paths;
CXXBasePath *Path = FindBestPath(S, EC, Entity.getNamingClass(),
- DeclaringClass, Paths);
+ DeclaringClass, DeclAccess, Paths);
if (!Path) {
// FIXME: delay dependent friendship
return;
}
- // Grab the access along the best path.
+ // Grab the access along the best path (note that this includes the
+ // final-step access).
AccessSpecifier NewAccess = Path->Access;
- if (Entity.isMemberAccess())
- NewAccess = CXXRecordDecl::MergeAccess(NewAccess, DeclAccess);
-
assert(NewAccess <= Access && "access along best path worse than direct?");
Access = NewAccess;
}
}
};
}
+
+// Make sure that friends have access to inherited protected members.
+namespace test2 {
+ struct X;
+
+ class ilist_half_node {
+ friend struct ilist_walker_bad;
+ X *Prev;
+ protected:
+ X *getPrev() { return Prev; }
+ };
+
+ class ilist_node : private ilist_half_node { // expected-note {{declared private here}} expected-note {{constrained by private inheritance here}}
+ friend struct ilist_walker;
+ X *Next;
+ X *getNext() { return Next; } // expected-note {{declared private here}}
+ };
+
+ struct X : ilist_node {};
+
+ struct ilist_walker {
+ static X *getPrev(X *N) { return N->getPrev(); }
+ static X *getNext(X *N) { return N->getNext(); }
+ };
+
+ struct ilist_walker_bad {
+ static X *getPrev(X *N) { return N->getPrev(); } // \
+ // expected-error {{'getPrev' is a private member of 'test2::ilist_half_node'}} \
+ // expected-error {{cannot cast 'test2::X' to its private base class 'test2::ilist_half_node'}}
+
+ static X *getNext(X *N) { return N->getNext(); } // \
+ // expected-error {{'getNext' is a private member of 'test2::ilist_node'}}
+ };
+}