/// Determines whether the accessed entity is accessible. Public members /// have been weeded out by this point. static AccessResult IsAccessible(Sema &S, const EffectiveContext &EC, AccessTarget &Entity) { // Determine the actual naming class. CXXRecordDecl *NamingClass = Entity.getNamingClass(); while (NamingClass->isAnonymousStructOrUnion()) NamingClass = cast<CXXRecordDecl>(NamingClass->getParent()); NamingClass = NamingClass->getCanonicalDecl(); AccessSpecifier UnprivilegedAccess = Entity.getAccess(); assert(UnprivilegedAccess != AS_public && "public access not weeded out"); // Before we try to recalculate access paths, try to white-list // accesses which just trade in on the final step, i.e. accesses // which don't require [M4] or [B4]. These are by far the most // common forms of privileged access. if (UnprivilegedAccess != AS_none) { switch (HasAccess(S, EC, NamingClass, UnprivilegedAccess, Entity)) { case AR_dependent: // This is actually an interesting policy decision. We don't // *have* to delay immediately here: we can do the full access // calculation in the hope that friendship on some intermediate // class will make the declaration accessible non-dependently. // But that's not cheap, and odds are very good (note: assertion // made without data) that the friend declaration will determine // access. return AR_dependent; case AR_accessible: return AR_accessible; case AR_inaccessible: break; } } AccessTarget::SavedInstanceContext _ = Entity.saveInstanceContext(); // We lower member accesses to base accesses by pretending that the // member is a base class of its declaring class. AccessSpecifier FinalAccess; if (Entity.isMemberAccess()) { // Determine if the declaration is accessible from EC when named // in its declaring class. NamedDecl *Target = Entity.getTargetDecl(); const CXXRecordDecl *DeclaringClass = Entity.getDeclaringClass(); FinalAccess = Target->getAccess(); switch (HasAccess(S, EC, DeclaringClass, FinalAccess, Entity)) { case AR_accessible: FinalAccess = AS_public; break; case AR_inaccessible: break; case AR_dependent: return AR_dependent; // see above } if (DeclaringClass == NamingClass) return (FinalAccess == AS_public ? AR_accessible : AR_inaccessible); Entity.suppressInstanceContext(); } else { FinalAccess = AS_public; } assert(Entity.getDeclaringClass() != NamingClass); // Append the declaration's access if applicable. CXXBasePaths Paths; CXXBasePath *Path = FindBestPath(S, EC, Entity, FinalAccess, Paths); if (!Path) return AR_dependent; assert(Path->Access <= UnprivilegedAccess && "access along best path worse than direct?"); if (Path->Access == AS_public) return AR_accessible; return AR_inaccessible; }
/// Diagnose the path which caused the given declaration or base class /// to become inaccessible. static void DiagnoseAccessPath(Sema &S, const EffectiveContext &EC, AccessTarget &Entity) { AccessSpecifier Access = Entity.getAccess(); const CXXRecordDecl *NamingClass = Entity.getNamingClass(); NamingClass = NamingClass->getCanonicalDecl(); NamedDecl *D = (Entity.isMemberAccess() ? Entity.getTargetDecl() : 0); const CXXRecordDecl *DeclaringClass = Entity.getDeclaringClass(); // Easy case: the decl's natural access determined its path access. // We have to check against AS_private here in case Access is AS_none, // indicating a non-public member of a private base class. if (D && (Access == D->getAccess() || D->getAccess() == AS_private)) { switch (HasAccess(S, EC, DeclaringClass, D->getAccess(), Entity)) { case AR_inaccessible: { S.Diag(D->getLocation(), diag::note_access_natural) << (unsigned) (Access == AS_protected) << /*FIXME: not implicitly*/ 0; return; } case AR_accessible: break; case AR_dependent: llvm_unreachable("can't diagnose dependent access failures"); return; } } CXXBasePaths Paths; CXXBasePath &Path = *FindBestPath(S, EC, Entity, AS_public, Paths); CXXBasePath::iterator I = Path.end(), E = Path.begin(); while (I != E) { --I; const CXXBaseSpecifier *BS = I->Base; AccessSpecifier BaseAccess = BS->getAccessSpecifier(); // If this is public inheritance, or the derived class is a friend, // skip this step. if (BaseAccess == AS_public) continue; switch (GetFriendKind(S, EC, I->Class)) { case AR_accessible: continue; case AR_inaccessible: break; case AR_dependent: llvm_unreachable("can't diagnose dependent access failures"); } // Check whether this base specifier is the tighest point // constraining access. We have to check against AS_private for // the same reasons as above. if (BaseAccess == AS_private || BaseAccess >= Access) { // We're constrained by inheritance, but we want to say // "declared private here" if we're diagnosing a hierarchy // conversion and this is the final step. unsigned diagnostic; if (D) diagnostic = diag::note_access_constrained_by_path; else if (I + 1 == Path.end()) diagnostic = diag::note_access_natural; else diagnostic = diag::note_access_constrained_by_path; S.Diag(BS->getSourceRange().getBegin(), diagnostic) << BS->getSourceRange() << (BaseAccess == AS_protected) << (BS->getAccessSpecifierAsWritten() == AS_none); return; } } llvm_unreachable("access not apparently constrained by path"); }