From: John McCall Date: Wed, 17 Mar 2010 04:58:56 +0000 (+0000) Subject: Grant nested classes the access privileges of their enclosing classes. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=88b6c71e3f0e68d094efcf880910caf424b46cbf;p=clang Grant nested classes the access privileges of their enclosing classes. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@98710 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/Sema/SemaAccess.cpp b/lib/Sema/SemaAccess.cpp index f0a38d5970..e7ec229d52 100644 --- a/lib/Sema/SemaAccess.cpp +++ b/lib/Sema/SemaAccess.cpp @@ -52,7 +52,7 @@ bool Sema::SetMemberAccessSpecifier(NamedDecl *MemberDecl, namespace { struct EffectiveContext { - EffectiveContext() : Record(0), Function(0) {} + EffectiveContext() : Function(0) {} explicit EffectiveContext(DeclContext *DC) { if (isa(DC)) { @@ -60,18 +60,28 @@ struct EffectiveContext { DC = Function->getDeclContext(); } else Function = 0; - - if (isa(DC)) - Record = cast(DC)->getCanonicalDecl(); - else - Record = 0; + + // C++ [class.access.nest]p1: + // A nested class is a member and as such has the same access + // rights as any other member. + // C++ [class.access]p2: + // A member of a class can also access all the names to which + // the class has access. + // This implies that the privileges of nesting are transitive. + while (isa(DC)) { + CXXRecordDecl *Record = cast(DC)->getCanonicalDecl(); + Records.push_back(Record); + DC = Record->getDeclContext(); + } } - bool isClass(const CXXRecordDecl *R) const { - return R->getCanonicalDecl() == Record; + bool includesClass(const CXXRecordDecl *R) const { + R = R->getCanonicalDecl(); + return std::find(Records.begin(), Records.end(), R) + != Records.end(); } - CXXRecordDecl *Record; + llvm::SmallVector Records; FunctionDecl *Function; }; } @@ -87,26 +97,32 @@ static Sema::AccessResult GetFriendKind(Sema &S, const EffectiveContext &EC, const CXXRecordDecl *Class) { // A class always has access to its own members. - if (EC.isClass(Class)) + if (EC.includesClass(Class)) return Sema::AR_accessible; + Sema::AccessResult OnFailure = Sema::AR_inaccessible; + // Okay, check friends. for (CXXRecordDecl::friend_iterator I = Class->friend_begin(), E = Class->friend_end(); I != E; ++I) { FriendDecl *Friend = *I; if (Type *T = Friend->getFriendType()) { - if (EC.Record && - S.Context.hasSameType(QualType(T, 0), - S.Context.getTypeDeclType(EC.Record))) - return Sema::AR_accessible; + CanQualType CT = T->getCanonicalTypeUnqualified(); + if (const RecordType *RT = CT->getAs()) + if (EC.includesClass(cast(RT->getDecl()))) + return Sema::AR_accessible; } else { NamedDecl *D = cast(Friend->getFriendDecl()->getCanonicalDecl()); // The decl pointers in EC have been canonicalized, so pointer // equality is sufficient. - if (D == EC.Function || D == EC.Record) + if (D == EC.Function) + return Sema::AR_accessible; + + if (isa(D) && + EC.includesClass(cast(D))) return Sema::AR_accessible; } @@ -114,7 +130,7 @@ static Sema::AccessResult GetFriendKind(Sema &S, } // That's it, give up. - return Sema::AR_inaccessible; + return OnFailure; } /// Finds the best path from the naming class to the declaring class, @@ -350,51 +366,38 @@ static Sema::AccessResult CheckEffectiveAccess(Sema &S, SourceLocation Loc, Sema::AccessedEntity const &Entity) { AccessSpecifier Access = Entity.getAccess(); - assert(Access != AS_public); + assert(Access != AS_public && "called for public access!"); + // Find a non-anonymous naming class. For records with access, + // there should always be one of these. CXXRecordDecl *NamingClass = Entity.getNamingClass(); while (NamingClass->isAnonymousStructOrUnion()) - // This should be guaranteed by the fact that the decl has - // non-public access. If not, we should make it guaranteed! NamingClass = cast(NamingClass->getParent()); - if (!EC.Record) { - TryElevateAccess(S, EC, Entity, Access); - if (Access == AS_public) return Sema::AR_accessible; - - if (!Entity.isQuiet()) - DiagnoseBadAccess(S, Loc, EC, NamingClass, Access, Entity); - return Sema::AR_inaccessible; - } - - // White-list accesses from within the declaring class. - if (Access != AS_none && EC.isClass(NamingClass)) + // White-list accesses from classes with privileges equivalent to the + // naming class --- but only if the access path isn't forbidden + // (i.e. an access of a private member from a subclass). + if (Access != AS_none && EC.includesClass(NamingClass)) return Sema::AR_accessible; - - // If the access is worse than 'protected', try to promote to it using - // friend declarations. - bool TriedElevation = false; - if (Access != AS_protected) { - TryElevateAccess(S, EC, Entity, Access); - if (Access == AS_public) return Sema::AR_accessible; - TriedElevation = true; - } + + // Try to elevate access. + // FIXME: delay if elevation was dependent? + // TODO: on some code, it might be better to do the protected check + // without trying to elevate first. + TryElevateAccess(S, EC, Entity, Access); + if (Access == AS_public) return Sema::AR_accessible; // Protected access. if (Access == AS_protected) { // FIXME: implement [class.protected]p1 - if (EC.Record->isDerivedFrom(NamingClass)) - return Sema::AR_accessible; + for (llvm::SmallVectorImpl::const_iterator + I = EC.Records.begin(), E = EC.Records.end(); I != E; ++I) + if ((*I)->isDerivedFrom(NamingClass)) + return Sema::AR_accessible; - // FIXME: delay dependent classes + // FIXME: delay if we can't decide class derivation yet. } - // We're about to reject; one last chance to promote access. - if (!TriedElevation) { - TryElevateAccess(S, EC, Entity, Access); - if (Access == AS_public) return Sema::AR_accessible; - } - // Okay, that's it, reject it. if (!Entity.isQuiet()) DiagnoseBadAccess(S, Loc, EC, NamingClass, Access, Entity); diff --git a/test/CXX/class.access/class.access.nest/p1.cpp b/test/CXX/class.access/class.access.nest/p1.cpp new file mode 100644 index 0000000000..d2644c6ef4 --- /dev/null +++ b/test/CXX/class.access/class.access.nest/p1.cpp @@ -0,0 +1,33 @@ +// RUN: %clang_cc1 -fsyntax-only -faccess-control -verify %s + +// Derived from GNU's std::string +namespace test0 { + class A { + struct B { + unsigned long length; + }; + struct C : B { + static const unsigned long max_length; + }; + }; + + const unsigned long A::C::max_length = sizeof(B); +} + +// Example from the standard. +namespace test1 { + class E { + int x; + class B {}; + + class I { + B b; + int y; // expected-note {{declared private here}} + void f(E* p, int i) { + p->x = i; + } + }; + + int g(I* p) { return p->y; } // expected-error {{'y' is a private member of 'test1::E::I'}} + }; +}