//===---- SemaAccess.cpp - C++ Access Control -------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file provides Sema routines for C++ access control semantics. // //===----------------------------------------------------------------------===// #include "clang/Basic/Specifiers.h" #include "clang/Sema/SemaInternal.h" #include "clang/AST/ASTContext.h" #include "clang/AST/CXXInheritance.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclFriend.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/DependentDiagnostic.h" #include "clang/AST/ExprCXX.h" #include "clang/Sema/DelayedDiagnostic.h" #include "clang/Sema/Initialization.h" #include "clang/Sema/Lookup.h" using namespace clang; using namespace sema; /// A copy of Sema's enum without AR_delayed. enum AccessResult { AR_accessible, AR_inaccessible, AR_dependent }; /// SetMemberAccessSpecifier - Set the access specifier of a member. /// Returns true on error (when the previous member decl access specifier /// is different from the new member decl access specifier). bool Sema::SetMemberAccessSpecifier(NamedDecl *MemberDecl, NamedDecl *PrevMemberDecl, AccessSpecifier LexicalAS) { if (!PrevMemberDecl) { // Use the lexical access specifier. MemberDecl->setAccess(LexicalAS); return false; } // C++ [class.access.spec]p3: When a member is redeclared its access // specifier must be same as its initial declaration. if (LexicalAS != AS_none && LexicalAS != PrevMemberDecl->getAccess()) { Diag(MemberDecl->getLocation(), diag::err_class_redeclared_with_different_access) << MemberDecl << LexicalAS; Diag(PrevMemberDecl->getLocation(), diag::note_previous_access_declaration) << PrevMemberDecl << PrevMemberDecl->getAccess(); MemberDecl->setAccess(LexicalAS); return true; } MemberDecl->setAccess(PrevMemberDecl->getAccess()); return false; } static CXXRecordDecl *FindDeclaringClass(NamedDecl *D) { DeclContext *DC = D->getDeclContext(); // This can only happen at top: enum decls only "publish" their // immediate members. if (isa(DC)) DC = cast(DC)->getDeclContext(); CXXRecordDecl *DeclaringClass = cast(DC); while (DeclaringClass->isAnonymousStructOrUnion()) DeclaringClass = cast(DeclaringClass->getDeclContext()); return DeclaringClass; } namespace { struct EffectiveContext { EffectiveContext() : Inner(nullptr), Dependent(false) {} explicit EffectiveContext(DeclContext *DC) : Inner(DC), Dependent(DC->isDependentContext()) { // C++11 [class.access.nest]p1: // A nested class is a member and as such has the same access // rights as any other member. // C++11 [class.access]p2: // A member of a class can also access all the names to which // the class has access. A local class of a member function // may access the same names that the member function itself // may access. // This almost implies that the privileges of nesting are transitive. // Technically it says nothing about the local classes of non-member // functions (which can gain privileges through friendship), but we // take that as an oversight. while (true) { // We want to add canonical declarations to the EC lists for // simplicity of checking, but we need to walk up through the // actual current DC chain. Otherwise, something like a local // extern or friend which happens to be the canonical // declaration will really mess us up. if (isa(DC)) { CXXRecordDecl *Record = cast(DC); Records.push_back(Record->getCanonicalDecl()); DC = Record->getDeclContext(); } else if (isa(DC)) { FunctionDecl *Function = cast(DC); Functions.push_back(Function->getCanonicalDecl()); if (Function->getFriendObjectKind()) DC = Function->getLexicalDeclContext(); else DC = Function->getDeclContext(); } else if (DC->isFileContext()) { break; } else { DC = DC->getParent(); } } } bool isDependent() const { return Dependent; } bool includesClass(const CXXRecordDecl *R) const { R = R->getCanonicalDecl(); return llvm::find(Records, R) != Records.end(); } /// Retrieves the innermost "useful" context. Can be null if we're /// doing access-control without privileges. DeclContext *getInnerContext() const { return Inner; } typedef SmallVectorImpl::const_iterator record_iterator; DeclContext *Inner; SmallVector Functions; SmallVector Records; bool Dependent; }; /// Like sema::AccessedEntity, but kindly lets us scribble all over /// it. struct AccessTarget : public AccessedEntity { AccessTarget(const AccessedEntity &Entity) : AccessedEntity(Entity) { initialize(); } AccessTarget(ASTContext &Context, MemberNonce _, CXXRecordDecl *NamingClass, DeclAccessPair FoundDecl, QualType BaseObjectType) : AccessedEntity(Context.getDiagAllocator(), Member, NamingClass, FoundDecl, BaseObjectType) { initialize(); } AccessTarget(ASTContext &Context, BaseNonce _, CXXRecordDecl *BaseClass, CXXRecordDecl *DerivedClass, AccessSpecifier Access) : AccessedEntity(Context.getDiagAllocator(), Base, BaseClass, DerivedClass, Access) { initialize(); } bool isInstanceMember() const { return (isMemberAccess() && getTargetDecl()->isCXXInstanceMember()); } bool hasInstanceContext() const { return HasInstanceContext; } class SavedInstanceContext { public: SavedInstanceContext(SavedInstanceContext &&S) : Target(S.Target), Has(S.Has) { S.Target = nullptr; } ~SavedInstanceContext() { if (Target) Target->HasInstanceContext = Has; } private: friend struct AccessTarget; explicit SavedInstanceContext(AccessTarget &Target) : Target(&Target), Has(Target.HasInstanceContext) {} AccessTarget *Target; bool Has; }; SavedInstanceContext saveInstanceContext() { return SavedInstanceContext(*this); } void suppressInstanceContext() { HasInstanceContext = false; } const CXXRecordDecl *resolveInstanceContext(Sema &S) const { assert(HasInstanceContext); if (CalculatedInstanceContext) return InstanceContext; CalculatedInstanceContext = true; DeclContext *IC = S.computeDeclContext(getBaseObjectType()); InstanceContext = (IC ? cast(IC)->getCanonicalDecl() : nullptr); return InstanceContext; } const CXXRecordDecl *getDeclaringClass() const { return DeclaringClass; } /// The "effective" naming class is the canonical non-anonymous /// class containing the actual naming class. const CXXRecordDecl *getEffectiveNamingClass() const { const CXXRecordDecl *namingClass = getNamingClass(); while (namingClass->isAnonymousStructOrUnion()) namingClass = cast(namingClass->getParent()); return namingClass->getCanonicalDecl(); } private: void initialize() { HasInstanceContext = (isMemberAccess() && !getBaseObjectType().isNull() && getTargetDecl()->isCXXInstanceMember()); CalculatedInstanceContext = false; InstanceContext = nullptr; if (isMemberAccess()) DeclaringClass = FindDeclaringClass(getTargetDecl()); else DeclaringClass = getBaseClass(); DeclaringClass = DeclaringClass->getCanonicalDecl(); } bool HasInstanceContext : 1; mutable bool CalculatedInstanceContext : 1; mutable const CXXRecordDecl *InstanceContext; const CXXRecordDecl *DeclaringClass; }; } /// Checks whether one class might instantiate to the other. static bool MightInstantiateTo(const CXXRecordDecl *From, const CXXRecordDecl *To) { // Declaration names are always preserved by instantiation. if (From->getDeclName() != To->getDeclName()) return false; const DeclContext *FromDC = From->getDeclContext()->getPrimaryContext(); const DeclContext *ToDC = To->getDeclContext()->getPrimaryContext(); if (FromDC == ToDC) return true; if (FromDC->isFileContext() || ToDC->isFileContext()) return false; // Be conservative. return true; } /// Checks whether one class is derived from another, inclusively. /// Properly indicates when it couldn't be determined due to /// dependence. /// /// This should probably be donated to AST or at least Sema. static AccessResult IsDerivedFromInclusive(const CXXRecordDecl *Derived, const CXXRecordDecl *Target) { assert(Derived->getCanonicalDecl() == Derived); assert(Target->getCanonicalDecl() == Target); if (Derived == Target) return AR_accessible; bool CheckDependent = Derived->isDependentContext(); if (CheckDependent && MightInstantiateTo(Derived, Target)) return AR_dependent; AccessResult OnFailure = AR_inaccessible; SmallVector Queue; // actually a stack while (true) { if (Derived->isDependentContext() && !Derived->hasDefinition() && !Derived->isLambda()) return AR_dependent; for (const auto &I : Derived->bases()) { const CXXRecordDecl *RD; QualType T = I.getType(); if (const RecordType *RT = T->getAs()) { RD = cast(RT->getDecl()); } else if (const InjectedClassNameType *IT = T->getAs()) { RD = IT->getDecl(); } else { assert(T->isDependentType() && "non-dependent base wasn't a record?"); OnFailure = AR_dependent; continue; } RD = RD->getCanonicalDecl(); if (RD == Target) return AR_accessible; if (CheckDependent && MightInstantiateTo(RD, Target)) OnFailure = AR_dependent; Queue.push_back(RD); } if (Queue.empty()) break; Derived = Queue.pop_back_val(); } return OnFailure; } static bool MightInstantiateTo(Sema &S, DeclContext *Context, DeclContext *Friend) { if (Friend == Context) return true; assert(!Friend->isDependentContext() && "can't handle friends with dependent contexts here"); if (!Context->isDependentContext()) return false; if (Friend->isFileContext()) return false; // TODO: this is very conservative return true; } // Asks whether the type in 'context' can ever instantiate to the type // in 'friend'. static bool MightInstantiateTo(Sema &S, CanQualType Context, CanQualType Friend) { if (Friend == Context) return true; if (!Friend->isDependentType() && !Context->isDependentType()) return false; // TODO: this is very conservative. return true; } static bool MightInstantiateTo(Sema &S, FunctionDecl *Context, FunctionDecl *Friend) { if (Context->getDeclName() != Friend->getDeclName()) return false; if (!MightInstantiateTo(S, Context->getDeclContext(), Friend->getDeclContext())) return false; CanQual FriendTy = S.Context.getCanonicalType(Friend->getType()) ->getAs(); CanQual ContextTy = S.Context.getCanonicalType(Context->getType()) ->getAs(); // There isn't any way that I know of to add qualifiers // during instantiation. if (FriendTy.getQualifiers() != ContextTy.getQualifiers()) return false; if (FriendTy->getNumParams() != ContextTy->getNumParams()) return false; if (!MightInstantiateTo(S, ContextTy->getReturnType(), FriendTy->getReturnType())) return false; for (unsigned I = 0, E = FriendTy->getNumParams(); I != E; ++I) if (!MightInstantiateTo(S, ContextTy->getParamType(I), FriendTy->getParamType(I))) return false; return true; } static bool MightInstantiateTo(Sema &S, FunctionTemplateDecl *Context, FunctionTemplateDecl *Friend) { return MightInstantiateTo(S, Context->getTemplatedDecl(), Friend->getTemplatedDecl()); } static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC, const CXXRecordDecl *Friend) { if (EC.includesClass(Friend)) return AR_accessible; if (EC.isDependent()) { for (const CXXRecordDecl *Context : EC.Records) { if (MightInstantiateTo(Context, Friend)) return AR_dependent; } } return AR_inaccessible; } static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC, CanQualType Friend) { if (const RecordType *RT = Friend->getAs()) return MatchesFriend(S, EC, cast(RT->getDecl())); // TODO: we can do better than this if (Friend->isDependentType()) return AR_dependent; return AR_inaccessible; } /// Determines whether the given friend class template matches /// anything in the effective context. static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC, ClassTemplateDecl *Friend) { AccessResult OnFailure = AR_inaccessible; // Check whether the friend is the template of a class in the // context chain. for (SmallVectorImpl::const_iterator I = EC.Records.begin(), E = EC.Records.end(); I != E; ++I) { CXXRecordDecl *Record = *I; // Figure out whether the current class has a template: ClassTemplateDecl *CTD; // A specialization of the template... if (isa(Record)) { CTD = cast(Record) ->getSpecializedTemplate(); // ... or the template pattern itself. } else { CTD = Record->getDescribedClassTemplate(); if (!CTD) continue; } // It's a match. if (Friend == CTD->getCanonicalDecl()) return AR_accessible; // If the context isn't dependent, it can't be a dependent match. if (!EC.isDependent()) continue; // If the template names don't match, it can't be a dependent // match. if (CTD->getDeclName() != Friend->getDeclName()) continue; // If the class's context can't instantiate to the friend's // context, it can't be a dependent match. if (!MightInstantiateTo(S, CTD->getDeclContext(), Friend->getDeclContext())) continue; // Otherwise, it's a dependent match. OnFailure = AR_dependent; } return OnFailure; } /// Determines whether the given friend function matches anything in /// the effective context. static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC, FunctionDecl *Friend) { AccessResult OnFailure = AR_inaccessible; for (SmallVectorImpl::const_iterator I = EC.Functions.begin(), E = EC.Functions.end(); I != E; ++I) { if (Friend == *I) return AR_accessible; if (EC.isDependent() && MightInstantiateTo(S, *I, Friend)) OnFailure = AR_dependent; } return OnFailure; } /// Determines whether the given friend function template matches /// anything in the effective context. static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC, FunctionTemplateDecl *Friend) { if (EC.Functions.empty()) return AR_inaccessible; AccessResult OnFailure = AR_inaccessible; for (SmallVectorImpl::const_iterator I = EC.Functions.begin(), E = EC.Functions.end(); I != E; ++I) { FunctionTemplateDecl *FTD = (*I)->getPrimaryTemplate(); if (!FTD) FTD = (*I)->getDescribedFunctionTemplate(); if (!FTD) continue; FTD = FTD->getCanonicalDecl(); if (Friend == FTD) return AR_accessible; if (EC.isDependent() && MightInstantiateTo(S, FTD, Friend)) OnFailure = AR_dependent; } return OnFailure; } /// Determines whether the given friend declaration matches anything /// in the effective context. static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC, FriendDecl *FriendD) { // Whitelist accesses if there's an invalid or unsupported friend // declaration. if (FriendD->isInvalidDecl() || FriendD->isUnsupportedFriend()) return AR_accessible; if (TypeSourceInfo *T = FriendD->getFriendType()) return MatchesFriend(S, EC, T->getType()->getCanonicalTypeUnqualified()); NamedDecl *Friend = cast(FriendD->getFriendDecl()->getCanonicalDecl()); // FIXME: declarations with dependent or templated scope. if (isa(Friend)) return MatchesFriend(S, EC, cast(Friend)); if (isa(Friend)) return MatchesFriend(S, EC, cast(Friend)); if (isa(Friend)) return MatchesFriend(S, EC, cast(Friend)); assert(isa(Friend) && "unknown friend decl kind"); return MatchesFriend(S, EC, cast(Friend)); } static AccessResult GetFriendKind(Sema &S, const EffectiveContext &EC, const CXXRecordDecl *Class) { AccessResult OnFailure = AR_inaccessible; // Okay, check friends. for (auto *Friend : Class->friends()) { switch (MatchesFriend(S, EC, Friend)) { case AR_accessible: return AR_accessible; case AR_inaccessible: continue; case AR_dependent: OnFailure = AR_dependent; break; } } // That's it, give up. return OnFailure; } namespace { /// A helper class for checking for a friend which will grant access /// to a protected instance member. struct ProtectedFriendContext { Sema &S; const EffectiveContext &EC; const CXXRecordDecl *NamingClass; bool CheckDependent; bool EverDependent; /// The path down to the current base class. SmallVector CurPath; ProtectedFriendContext(Sema &S, const EffectiveContext &EC, const CXXRecordDecl *InstanceContext, const CXXRecordDecl *NamingClass) : S(S), EC(EC), NamingClass(NamingClass), CheckDependent(InstanceContext->isDependentContext() || NamingClass->isDependentContext()), EverDependent(false) {} /// Check classes in the current path for friendship, starting at /// the given index. bool checkFriendshipAlongPath(unsigned I) { assert(I < CurPath.size()); for (unsigned E = CurPath.size(); I != E; ++I) { switch (GetFriendKind(S, EC, CurPath[I])) { case AR_accessible: return true; case AR_inaccessible: continue; case AR_dependent: EverDependent = true; continue; } } return false; } /// Perform a search starting at the given class. /// /// PrivateDepth is the index of the last (least derived) class /// along the current path such that a notional public member of /// the final class in the path would have access in that class. bool findFriendship(const CXXRecordDecl *Cur, unsigned PrivateDepth) { // If we ever reach the naming class, check the current path for // friendship. We can also stop recursing because we obviously // won't find the naming class there again. if (Cur == NamingClass) return checkFriendshipAlongPath(PrivateDepth); if (CheckDependent && MightInstantiateTo(Cur, NamingClass)) EverDependent = true; // Recurse into the base classes. for (const auto &I : Cur->bases()) { // If this is private inheritance, then a public member of the // base will not have any access in classes derived from Cur. unsigned BasePrivateDepth = PrivateDepth; if (I.getAccessSpecifier() == AS_private) BasePrivateDepth = CurPath.size() - 1; const CXXRecordDecl *RD; QualType T = I.getType(); if (const RecordType *RT = T->getAs()) { RD = cast(RT->getDecl()); } else if (const InjectedClassNameType *IT = T->getAs()) { RD = IT->getDecl(); } else { assert(T->isDependentType() && "non-dependent base wasn't a record?"); EverDependent = true; continue; } // Recurse. We don't need to clean up if this returns true. CurPath.push_back(RD); if (findFriendship(RD->getCanonicalDecl(), BasePrivateDepth)) return true; CurPath.pop_back(); } return false; } bool findFriendship(const CXXRecordDecl *Cur) { assert(CurPath.empty()); CurPath.push_back(Cur); return findFriendship(Cur, 0); } }; } /// Search for a class P that EC is a friend of, under the constraint /// InstanceContext <= P /// if InstanceContext exists, or else /// NamingClass <= P /// and with the additional restriction that a protected member of /// NamingClass would have some natural access in P, which implicitly /// imposes the constraint that P <= NamingClass. /// /// This isn't quite the condition laid out in the standard. /// Instead of saying that a notional protected member of NamingClass /// would have to have some natural access in P, it says the actual /// target has to have some natural access in P, which opens up the /// possibility that the target (which is not necessarily a member /// of NamingClass) might be more accessible along some path not /// passing through it. That's really a bad idea, though, because it /// introduces two problems: /// - Most importantly, it breaks encapsulation because you can /// access a forbidden base class's members by directly subclassing /// it elsewhere. /// - It also makes access substantially harder to compute because it /// breaks the hill-climbing algorithm: knowing that the target is /// accessible in some base class would no longer let you change /// the question solely to whether the base class is accessible, /// because the original target might have been more accessible /// because of crazy subclassing. /// So we don't implement that. static AccessResult GetProtectedFriendKind(Sema &S, const EffectiveContext &EC, const CXXRecordDecl *InstanceContext, const CXXRecordDecl *NamingClass) { assert(InstanceContext == nullptr || InstanceContext->getCanonicalDecl() == InstanceContext); assert(NamingClass->getCanonicalDecl() == NamingClass); // If we don't have an instance context, our constraints give us // that NamingClass <= P <= NamingClass, i.e. P == NamingClass. // This is just the usual friendship check. if (!InstanceContext) return GetFriendKind(S, EC, NamingClass); ProtectedFriendContext PRC(S, EC, InstanceContext, NamingClass); if (PRC.findFriendship(InstanceContext)) return AR_accessible; if (PRC.EverDependent) return AR_dependent; return AR_inaccessible; } static AccessResult HasAccess(Sema &S, const EffectiveContext &EC, const CXXRecordDecl *NamingClass, AccessSpecifier Access, const AccessTarget &Target) { assert(NamingClass->getCanonicalDecl() == NamingClass && "declaration should be canonicalized before being passed here"); if (Access == AS_public) return AR_accessible; assert(Access == AS_private || Access == AS_protected); AccessResult OnFailure = AR_inaccessible; for (EffectiveContext::record_iterator I = EC.Records.begin(), E = EC.Records.end(); I != E; ++I) { // All the declarations in EC have been canonicalized, so pointer // equality from this point on will work fine. const CXXRecordDecl *ECRecord = *I; // [B2] and [M2] if (Access == AS_private) { if (ECRecord == NamingClass) return AR_accessible; if (EC.isDependent() && MightInstantiateTo(ECRecord, NamingClass)) OnFailure = AR_dependent; // [B3] and [M3] } else { assert(Access == AS_protected); switch (IsDerivedFromInclusive(ECRecord, NamingClass)) { case AR_accessible: break; case AR_inaccessible: continue; case AR_dependent: OnFailure = AR_dependent; continue; } // C++ [class.protected]p1: // An additional access check beyond those described earlier in // [class.access] is applied when a non-static data member or // non-static member function is a protected member of its naming // class. As described earlier, access to a protected member is // granted because the reference occurs in a friend or member of // some class C. If the access is to form a pointer to member, // the nested-name-specifier shall name C or a class derived from // C. All other accesses involve a (possibly implicit) object // expression. In this case, the class of the object expression // shall be C or a class derived from C. // // We interpret this as a restriction on [M3]. // In this part of the code, 'C' is just our context class ECRecord. // These rules are different if we don't have an instance context. if (!Target.hasInstanceContext()) { // If it's not an instance member, these restrictions don't apply. if (!Target.isInstanceMember()) return AR_accessible; // If it's an instance member, use the pointer-to-member rule // that the naming class has to be derived from the effective // context. // Emulate a MSVC bug where the creation of pointer-to-member // to protected member of base class is allowed but only from // static member functions. if (S.getLangOpts().MSVCCompat && !EC.Functions.empty()) if (CXXMethodDecl* MD = dyn_cast(EC.Functions.front())) if (MD->isStatic()) return AR_accessible; // Despite the standard's confident wording, there is a case // where you can have an instance member that's neither in a // pointer-to-member expression nor in a member access: when // it names a field in an unevaluated context that can't be an // implicit member. Pending clarification, we just apply the // same naming-class restriction here. // FIXME: we're probably not correctly adding the // protected-member restriction when we retroactively convert // an expression to being evaluated. // We know that ECRecord derives from NamingClass. The // restriction says to check whether NamingClass derives from // ECRecord, but that's not really necessary: two distinct // classes can't be recursively derived from each other. So // along this path, we just need to check whether the classes // are equal. if (NamingClass == ECRecord) return AR_accessible; // Otherwise, this context class tells us nothing; on to the next. continue; } assert(Target.isInstanceMember()); const CXXRecordDecl *InstanceContext = Target.resolveInstanceContext(S); if (!InstanceContext) { OnFailure = AR_dependent; continue; } switch (IsDerivedFromInclusive(InstanceContext, ECRecord)) { case AR_accessible: return AR_accessible; case AR_inaccessible: continue; case AR_dependent: OnFailure = AR_dependent; continue; } } } // [M3] and [B3] say that, if the target is protected in N, we grant // access if the access occurs in a friend or member of some class P // that's a subclass of N and where the target has some natural // access in P. The 'member' aspect is easy to handle because P // would necessarily be one of the effective-context records, and we // address that above. The 'friend' aspect is completely ridiculous // to implement because there are no restrictions at all on P // *unless* the [class.protected] restriction applies. If it does, // however, we should ignore whether the naming class is a friend, // and instead rely on whether any potential P is a friend. if (Access == AS_protected && Target.isInstanceMember()) { // Compute the instance context if possible. const CXXRecordDecl *InstanceContext = nullptr; if (Target.hasInstanceContext()) { InstanceContext = Target.resolveInstanceContext(S); if (!InstanceContext) return AR_dependent; } switch (GetProtectedFriendKind(S, EC, InstanceContext, NamingClass)) { case AR_accessible: return AR_accessible; case AR_inaccessible: return OnFailure; case AR_dependent: return AR_dependent; } llvm_unreachable("impossible friendship kind"); } switch (GetFriendKind(S, EC, NamingClass)) { case AR_accessible: return AR_accessible; case AR_inaccessible: return OnFailure; case AR_dependent: return AR_dependent; } // Silence bogus warnings llvm_unreachable("impossible friendship kind"); } /// Finds the best path from the naming class to the declaring class, /// taking friend declarations into account. /// /// C++0x [class.access.base]p5: /// A member m is accessible at the point R when named in class N if /// [M1] m as a member of N is public, or /// [M2] m as a member of N is private, and R occurs in a member or /// friend of class N, or /// [M3] 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, or /// [M4] there exists a base class B of N that is accessible at R, and /// m is accessible at R when named in class B. /// /// C++0x [class.access.base]p4: /// A base class B of N is accessible at R, if /// [B1] an invented public member of B would be a public member of N, or /// [B2] R occurs in a member or friend of class N, and an invented public /// member of B would be a private or protected member of N, or /// [B3] R occurs in a member or friend of a class P derived from N, and an /// invented public member of B would be a private or protected member /// of P, or /// [B4] there exists a class S such that B is a base class of S accessible /// at R and S is a base class of N accessible at R. /// /// Along a single inheritance path we can restate both of these /// iteratively: /// /// First, we note that M1-4 are equivalent to B1-4 if the member is /// treated as a notional base of its declaring class with inheritance /// access equivalent to the member's access. Therefore we need only /// ask whether a class B is accessible from a class N in context R. /// /// Let B_1 .. B_n be the inheritance path in question (i.e. where /// B_1 = N, B_n = B, and for all i, B_{i+1} is a direct base class of /// B_i). For i in 1..n, we will calculate ACAB(i), the access to the /// closest accessible base in the path: /// Access(a, b) = (* access on the base specifier from a to b *) /// Merge(a, forbidden) = forbidden /// Merge(a, private) = forbidden /// Merge(a, b) = min(a,b) /// Accessible(c, forbidden) = false /// Accessible(c, private) = (R is c) || IsFriend(c, R) /// Accessible(c, protected) = (R derived from c) || IsFriend(c, R) /// Accessible(c, public) = true /// ACAB(n) = public /// ACAB(i) = /// let AccessToBase = Merge(Access(B_i, B_{i+1}), ACAB(i+1)) in /// if Accessible(B_i, AccessToBase) then public else AccessToBase /// /// B is an accessible base of N at R iff ACAB(1) = public. /// /// \param FinalAccess the access of the "final step", or AS_public if /// there is no final step. /// \return null if friendship is dependent static CXXBasePath *FindBestPath(Sema &S, const EffectiveContext &EC, AccessTarget &Target, AccessSpecifier FinalAccess, CXXBasePaths &Paths) { // Derive the paths to the desired base. const CXXRecordDecl *Derived = Target.getNamingClass(); const CXXRecordDecl *Base = Target.getDeclaringClass(); // FIXME: fail correctly when there are dependent paths. bool isDerived = Derived->isDerivedFrom(const_cast(Base), Paths); assert(isDerived && "derived class not actually derived from base"); (void) isDerived; CXXBasePath *BestPath = nullptr; assert(FinalAccess != AS_none && "forbidden access after declaring class"); bool AnyDependent = false; // Derive the friend-modified access along each path. for (CXXBasePaths::paths_iterator PI = Paths.begin(), PE = Paths.end(); PI != PE; ++PI) { AccessTarget::SavedInstanceContext _ = Target.saveInstanceContext(); // Walk through the path backwards. 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; } const CXXRecordDecl *NC = I->Class->getCanonicalDecl(); AccessSpecifier BaseAccess = I->Base->getAccessSpecifier(); PathAccess = std::max(PathAccess, BaseAccess); switch (HasAccess(S, EC, NC, PathAccess, Target)) { case AR_inaccessible: break; case AR_accessible: PathAccess = AS_public; // Future tests are not against members and so do not have // instance context. Target.suppressInstanceContext(); break; case AR_dependent: AnyDependent = true; goto Next; } } // Note that we modify the path's Access field to the // friend-modified access. if (BestPath == nullptr || PathAccess < BestPath->Access) { BestPath = &*PI; BestPath->Access = PathAccess; // Short-circuit if we found a public path. if (BestPath->Access == AS_public) return BestPath; } Next: ; } assert((!BestPath || BestPath->Access != AS_public) && "fell out of loop with public path"); // We didn't find a public path, but at least one path was subject // to dependent friendship, so delay the check. if (AnyDependent) return nullptr; return BestPath; } /// Given that an entity has protected natural access, check whether /// access might be denied because of the protected member access /// restriction. /// /// \return true if a note was emitted static bool TryDiagnoseProtectedAccess(Sema &S, const EffectiveContext &EC, AccessTarget &Target) { // Only applies to instance accesses. if (!Target.isInstanceMember()) return false; assert(Target.isMemberAccess()); const CXXRecordDecl *NamingClass = Target.getEffectiveNamingClass(); for (EffectiveContext::record_iterator I = EC.Records.begin(), E = EC.Records.end(); I != E; ++I) { const CXXRecordDecl *ECRecord = *I; switch (IsDerivedFromInclusive(ECRecord, NamingClass)) { case AR_accessible: break; case AR_inaccessible: continue; case AR_dependent: continue; } // The effective context is a subclass of the declaring class. // Check whether the [class.protected] restriction is limiting // access. // To get this exactly right, this might need to be checked more // holistically; it's not necessarily the case that gaining // access here would grant us access overall. NamedDecl *D = Target.getTargetDecl(); // If we don't have an instance context, [class.protected] says the // naming class has to equal the context class. if (!Target.hasInstanceContext()) { // If it does, the restriction doesn't apply. if (NamingClass == ECRecord) continue; // TODO: it would be great to have a fixit here, since this is // such an obvious error. S.Diag(D->getLocation(), diag::note_access_protected_restricted_noobject) << S.Context.getTypeDeclType(ECRecord); return true; } const CXXRecordDecl *InstanceContext = Target.resolveInstanceContext(S); assert(InstanceContext && "diagnosing dependent access"); switch (IsDerivedFromInclusive(InstanceContext, ECRecord)) { case AR_accessible: continue; case AR_dependent: continue; case AR_inaccessible: break; } // Okay, the restriction seems to be what's limiting us. // Use a special diagnostic for constructors and destructors. if (isa(D) || isa(D) || (isa(D) && isa( cast(D)->getTemplatedDecl()))) { return S.Diag(D->getLocation(), diag::note_access_protected_restricted_ctordtor) << isa(D->getAsFunction()); } // Otherwise, use the generic diagnostic. return S.Diag(D->getLocation(), diag::note_access_protected_restricted_object) << S.Context.getTypeDeclType(ECRecord); } return false; } /// We are unable to access a given declaration due to its direct /// access control; diagnose that. static void diagnoseBadDirectAccess(Sema &S, const EffectiveContext &EC, AccessTarget &entity) { assert(entity.isMemberAccess()); NamedDecl *D = entity.getTargetDecl(); if (D->getAccess() == AS_protected && TryDiagnoseProtectedAccess(S, EC, entity)) return; // Find an original declaration. while (D->isOutOfLine()) { NamedDecl *PrevDecl = nullptr; if (VarDecl *VD = dyn_cast(D)) PrevDecl = VD->getPreviousDecl(); else if (FunctionDecl *FD = dyn_cast(D)) PrevDecl = FD->getPreviousDecl(); else if (TypedefNameDecl *TND = dyn_cast(D)) PrevDecl = TND->getPreviousDecl(); else if (TagDecl *TD = dyn_cast(D)) { if (isa(D) && cast(D)->isInjectedClassName()) break; PrevDecl = TD->getPreviousDecl(); } if (!PrevDecl) break; D = PrevDecl; } CXXRecordDecl *DeclaringClass = FindDeclaringClass(D); Decl *ImmediateChild; if (D->getDeclContext() == DeclaringClass) ImmediateChild = D; else { DeclContext *DC = D->getDeclContext(); while (DC->getParent() != DeclaringClass) DC = DC->getParent(); ImmediateChild = cast(DC); } // Check whether there's an AccessSpecDecl preceding this in the // chain of the DeclContext. bool isImplicit = true; for (const auto *I : DeclaringClass->decls()) { if (I == ImmediateChild) break; if (isa(I)) { isImplicit = false; break; } } S.Diag(D->getLocation(), diag::note_access_natural) << (unsigned) (D->getAccess() == AS_protected) << isImplicit; } /// Diagnose the path which caused the given declaration or base class /// to become inaccessible. static void DiagnoseAccessPath(Sema &S, const EffectiveContext &EC, AccessTarget &entity) { // Save the instance context to preserve invariants. AccessTarget::SavedInstanceContext _ = entity.saveInstanceContext(); // This basically repeats the main algorithm but keeps some more // information. // The natural access so far. AccessSpecifier accessSoFar = AS_public; // Check whether we have special rights to the declaring class. if (entity.isMemberAccess()) { NamedDecl *D = entity.getTargetDecl(); accessSoFar = D->getAccess(); const CXXRecordDecl *declaringClass = entity.getDeclaringClass(); switch (HasAccess(S, EC, declaringClass, accessSoFar, entity)) { // If the declaration is accessible when named in its declaring // class, then we must be constrained by the path. case AR_accessible: accessSoFar = AS_public; entity.suppressInstanceContext(); break; case AR_inaccessible: if (accessSoFar == AS_private || declaringClass == entity.getEffectiveNamingClass()) return diagnoseBadDirectAccess(S, EC, entity); break; case AR_dependent: llvm_unreachable("cannot diagnose dependent access"); } } CXXBasePaths paths; CXXBasePath &path = *FindBestPath(S, EC, entity, accessSoFar, paths); assert(path.Access != AS_public); CXXBasePath::iterator i = path.end(), e = path.begin(); CXXBasePath::iterator constrainingBase = i; while (i != e) { --i; assert(accessSoFar != AS_none && accessSoFar != AS_private); // Is the entity accessible when named in the deriving class, as // modified by the base specifier? const CXXRecordDecl *derivingClass = i->Class->getCanonicalDecl(); const CXXBaseSpecifier *base = i->Base; // If the access to this base is worse than the access we have to // the declaration, remember it. AccessSpecifier baseAccess = base->getAccessSpecifier(); if (baseAccess > accessSoFar) { constrainingBase = i; accessSoFar = baseAccess; } switch (HasAccess(S, EC, derivingClass, accessSoFar, entity)) { case AR_inaccessible: break; case AR_accessible: accessSoFar = AS_public; entity.suppressInstanceContext(); constrainingBase = nullptr; break; case AR_dependent: llvm_unreachable("cannot diagnose dependent access"); } // If this was private inheritance, but we don't have access to // the deriving class, we're done. if (accessSoFar == AS_private) { assert(baseAccess == AS_private); assert(constrainingBase == i); break; } } // If we don't have a constraining base, the access failure must be // due to the original declaration. if (constrainingBase == path.end()) return diagnoseBadDirectAccess(S, EC, entity); // 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 (entity.isMemberAccess() || constrainingBase + 1 != path.end()) { diagnostic = diag::note_access_constrained_by_path; } else { diagnostic = diag::note_access_natural; } const CXXBaseSpecifier *base = constrainingBase->Base; S.Diag(base->getSourceRange().getBegin(), diagnostic) << base->getSourceRange() << (base->getAccessSpecifier() == AS_protected) << (base->getAccessSpecifierAsWritten() == AS_none); if (entity.isMemberAccess()) S.Diag(entity.getTargetDecl()->getLocation(), diag::note_member_declared_at); } static void DiagnoseBadAccess(Sema &S, SourceLocation Loc, const EffectiveContext &EC, AccessTarget &Entity) { const CXXRecordDecl *NamingClass = Entity.getNamingClass(); const CXXRecordDecl *DeclaringClass = Entity.getDeclaringClass(); NamedDecl *D = (Entity.isMemberAccess() ? Entity.getTargetDecl() : nullptr); S.Diag(Loc, Entity.getDiag()) << (Entity.getAccess() == AS_protected) << (D ? D->getDeclName() : DeclarationName()) << S.Context.getTypeDeclType(NamingClass) << S.Context.getTypeDeclType(DeclaringClass); DiagnoseAccessPath(S, EC, Entity); } /// MSVC has a bug where if during an using declaration name lookup, /// the declaration found is unaccessible (private) and that declaration /// was bring into scope via another using declaration whose target /// declaration is accessible (public) then no error is generated. /// Example: /// class A { /// public: /// int f(); /// }; /// class B : public A { /// private: /// using A::f; /// }; /// class C : public B { /// private: /// using B::f; /// }; /// /// Here, B::f is private so this should fail in Standard C++, but /// because B::f refers to A::f which is public MSVC accepts it. static bool IsMicrosoftUsingDeclarationAccessBug(Sema& S, SourceLocation AccessLoc, AccessTarget &Entity) { if (UsingShadowDecl *Shadow = dyn_cast(Entity.getTargetDecl())) { const NamedDecl *OrigDecl = Entity.getTargetDecl()->getUnderlyingDecl(); if (Entity.getTargetDecl()->getAccess() == AS_private && (OrigDecl->getAccess() == AS_public || OrigDecl->getAccess() == AS_protected)) { S.Diag(AccessLoc, diag::ext_ms_using_declaration_inaccessible) << Shadow->getUsingDecl()->getQualifiedNameAsString() << OrigDecl->getQualifiedNameAsString(); return true; } } return false; } /// 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. const CXXRecordDecl *NamingClass = Entity.getEffectiveNamingClass(); 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: // Target is accessible at EC when named in its declaring class. // We can now hill-climb and simply check whether the declaring // class is accessible as a base of the naming class. This is // equivalent to checking the access of a notional public // member with no instance context. FinalAccess = AS_public; Entity.suppressInstanceContext(); break; case AR_inaccessible: break; case AR_dependent: return AR_dependent; // see above } if (DeclaringClass == NamingClass) return (FinalAccess == AS_public ? AR_accessible : AR_inaccessible); } 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; } static void DelayDependentAccess(Sema &S, const EffectiveContext &EC, SourceLocation Loc, const AccessTarget &Entity) { assert(EC.isDependent() && "delaying non-dependent access"); DeclContext *DC = EC.getInnerContext(); assert(DC->isDependentContext() && "delaying non-dependent access"); DependentDiagnostic::Create(S.Context, DC, DependentDiagnostic::Access, Loc, Entity.isMemberAccess(), Entity.getAccess(), Entity.getTargetDecl(), Entity.getNamingClass(), Entity.getBaseObjectType(), Entity.getDiag()); } /// Checks access to an entity from the given effective context. static AccessResult CheckEffectiveAccess(Sema &S, const EffectiveContext &EC, SourceLocation Loc, AccessTarget &Entity) { assert(Entity.getAccess() != AS_public && "called for public access!"); switch (IsAccessible(S, EC, Entity)) { case AR_dependent: DelayDependentAccess(S, EC, Loc, Entity); return AR_dependent; case AR_inaccessible: if (S.getLangOpts().MSVCCompat && IsMicrosoftUsingDeclarationAccessBug(S, Loc, Entity)) return AR_accessible; if (!Entity.isQuiet()) DiagnoseBadAccess(S, Loc, EC, Entity); return AR_inaccessible; case AR_accessible: return AR_accessible; } // silence unnecessary warning llvm_unreachable("invalid access result"); } static Sema::AccessResult CheckAccess(Sema &S, SourceLocation Loc, AccessTarget &Entity) { // If the access path is public, it's accessible everywhere. if (Entity.getAccess() == AS_public) return Sema::AR_accessible; // If we're currently parsing a declaration, we may need to delay // access control checking, because our effective context might be // different based on what the declaration comes out as. // // For example, we might be parsing a declaration with a scope // specifier, like this: // A::private_type A::foo() { ... } // // Or we might be parsing something that will turn out to be a friend: // void foo(A::private_type); // void B::foo(A::private_type); if (S.DelayedDiagnostics.shouldDelayDiagnostics()) { S.DelayedDiagnostics.add(DelayedDiagnostic::makeAccess(Loc, Entity)); return Sema::AR_delayed; } EffectiveContext EC(S.CurContext); switch (CheckEffectiveAccess(S, EC, Loc, Entity)) { case AR_accessible: return Sema::AR_accessible; case AR_inaccessible: return Sema::AR_inaccessible; case AR_dependent: return Sema::AR_dependent; } llvm_unreachable("invalid access result"); } void Sema::HandleDelayedAccessCheck(DelayedDiagnostic &DD, Decl *D) { // Access control for names used in the declarations of functions // and function templates should normally be evaluated in the context // of the declaration, just in case it's a friend of something. // However, this does not apply to local extern declarations. DeclContext *DC = D->getDeclContext(); if (D->isLocalExternDecl()) { DC = D->getLexicalDeclContext(); } else if (FunctionDecl *FN = dyn_cast(D)) { DC = FN; } else if (TemplateDecl *TD = dyn_cast(D)) { if (isa(TD->getTemplatedDecl())) DC = cast(TD->getTemplatedDecl()); } EffectiveContext EC(DC); AccessTarget Target(DD.getAccessData()); if (CheckEffectiveAccess(*this, EC, DD.Loc, Target) == ::AR_inaccessible) DD.Triggered = true; } void Sema::HandleDependentAccessCheck(const DependentDiagnostic &DD, const MultiLevelTemplateArgumentList &TemplateArgs) { SourceLocation Loc = DD.getAccessLoc(); AccessSpecifier Access = DD.getAccess(); Decl *NamingD = FindInstantiatedDecl(Loc, DD.getAccessNamingClass(), TemplateArgs); if (!NamingD) return; Decl *TargetD = FindInstantiatedDecl(Loc, DD.getAccessTarget(), TemplateArgs); if (!TargetD) return; if (DD.isAccessToMember()) { CXXRecordDecl *NamingClass = cast(NamingD); NamedDecl *TargetDecl = cast(TargetD); QualType BaseObjectType = DD.getAccessBaseObjectType(); if (!BaseObjectType.isNull()) { BaseObjectType = SubstType(BaseObjectType, TemplateArgs, Loc, DeclarationName()); if (BaseObjectType.isNull()) return; } AccessTarget Entity(Context, AccessTarget::Member, NamingClass, DeclAccessPair::make(TargetDecl, Access), BaseObjectType); Entity.setDiag(DD.getDiagnostic()); CheckAccess(*this, Loc, Entity); } else { AccessTarget Entity(Context, AccessTarget::Base, cast(TargetD), cast(NamingD), Access); Entity.setDiag(DD.getDiagnostic()); CheckAccess(*this, Loc, Entity); } } Sema::AccessResult Sema::CheckUnresolvedLookupAccess(UnresolvedLookupExpr *E, DeclAccessPair Found) { if (!getLangOpts().AccessControl || !E->getNamingClass() || Found.getAccess() == AS_public) return AR_accessible; AccessTarget Entity(Context, AccessTarget::Member, E->getNamingClass(), Found, QualType()); Entity.setDiag(diag::err_access) << E->getSourceRange(); return CheckAccess(*this, E->getNameLoc(), Entity); } /// Perform access-control checking on a previously-unresolved member /// access which has now been resolved to a member. Sema::AccessResult Sema::CheckUnresolvedMemberAccess(UnresolvedMemberExpr *E, DeclAccessPair Found) { if (!getLangOpts().AccessControl || Found.getAccess() == AS_public) return AR_accessible; QualType BaseType = E->getBaseType(); if (E->isArrow()) BaseType = BaseType->castAs()->getPointeeType(); AccessTarget Entity(Context, AccessTarget::Member, E->getNamingClass(), Found, BaseType); Entity.setDiag(diag::err_access) << E->getSourceRange(); return CheckAccess(*this, E->getMemberLoc(), Entity); } /// Is the given member accessible for the purposes of deciding whether to /// define a special member function as deleted? bool Sema::isMemberAccessibleForDeletion(CXXRecordDecl *NamingClass, DeclAccessPair Found, QualType ObjectType, SourceLocation Loc, const PartialDiagnostic &Diag) { // Fast path. if (Found.getAccess() == AS_public || !getLangOpts().AccessControl) return true; AccessTarget Entity(Context, AccessTarget::Member, NamingClass, Found, ObjectType); // Suppress diagnostics. Entity.setDiag(Diag); switch (CheckAccess(*this, Loc, Entity)) { case AR_accessible: return true; case AR_inaccessible: return false; case AR_dependent: llvm_unreachable("dependent for =delete computation"); case AR_delayed: llvm_unreachable("cannot delay =delete computation"); } llvm_unreachable("bad access result"); } Sema::AccessResult Sema::CheckDestructorAccess(SourceLocation Loc, CXXDestructorDecl *Dtor, const PartialDiagnostic &PDiag, QualType ObjectTy) { if (!getLangOpts().AccessControl) return AR_accessible; // There's never a path involved when checking implicit destructor access. AccessSpecifier Access = Dtor->getAccess(); if (Access == AS_public) return AR_accessible; CXXRecordDecl *NamingClass = Dtor->getParent(); if (ObjectTy.isNull()) ObjectTy = Context.getTypeDeclType(NamingClass); AccessTarget Entity(Context, AccessTarget::Member, NamingClass, DeclAccessPair::make(Dtor, Access), ObjectTy); Entity.setDiag(PDiag); // TODO: avoid copy return CheckAccess(*this, Loc, Entity); } /// Checks access to a constructor. Sema::AccessResult Sema::CheckConstructorAccess(SourceLocation UseLoc, CXXConstructorDecl *Constructor, DeclAccessPair Found, const InitializedEntity &Entity, bool IsCopyBindingRefToTemp) { if (!getLangOpts().AccessControl || Found.getAccess() == AS_public) return AR_accessible; PartialDiagnostic PD(PDiag()); switch (Entity.getKind()) { default: PD = PDiag(IsCopyBindingRefToTemp ? diag::ext_rvalue_to_reference_access_ctor : diag::err_access_ctor); break; case InitializedEntity::EK_Base: PD = PDiag(diag::err_access_base_ctor); PD << Entity.isInheritedVirtualBase() << Entity.getBaseSpecifier()->getType() << getSpecialMember(Constructor); break; case InitializedEntity::EK_Member: { const FieldDecl *Field = cast(Entity.getDecl()); PD = PDiag(diag::err_access_field_ctor); PD << Field->getType() << getSpecialMember(Constructor); break; } case InitializedEntity::EK_LambdaCapture: { StringRef VarName = Entity.getCapturedVarName(); PD = PDiag(diag::err_access_lambda_capture); PD << VarName << Entity.getType() << getSpecialMember(Constructor); break; } } return CheckConstructorAccess(UseLoc, Constructor, Found, Entity, PD); } /// Checks access to a constructor. Sema::AccessResult Sema::CheckConstructorAccess(SourceLocation UseLoc, CXXConstructorDecl *Constructor, DeclAccessPair Found, const InitializedEntity &Entity, const PartialDiagnostic &PD) { if (!getLangOpts().AccessControl || Found.getAccess() == AS_public) return AR_accessible; CXXRecordDecl *NamingClass = Constructor->getParent(); // Initializing a base sub-object is an instance method call on an // object of the derived class. Otherwise, we have an instance method // call on an object of the constructed type. // // FIXME: If we have a parent, we're initializing the base class subobject // in aggregate initialization. It's not clear whether the object class // should be the base class or the derived class in that case. CXXRecordDecl *ObjectClass; if ((Entity.getKind() == InitializedEntity::EK_Base || Entity.getKind() == InitializedEntity::EK_Delegating) && !Entity.getParent()) { ObjectClass = cast(CurContext)->getParent(); } else if (auto *Shadow = dyn_cast(Found.getDecl())) { // If we're using an inheriting constructor to construct an object, // the object class is the derived class, not the base class. ObjectClass = Shadow->getParent(); } else { ObjectClass = NamingClass; } AccessTarget AccessEntity( Context, AccessTarget::Member, NamingClass, DeclAccessPair::make(Constructor, Found.getAccess()), Context.getTypeDeclType(ObjectClass)); AccessEntity.setDiag(PD); return CheckAccess(*this, UseLoc, AccessEntity); } /// Checks access to an overloaded operator new or delete. Sema::AccessResult Sema::CheckAllocationAccess(SourceLocation OpLoc, SourceRange PlacementRange, CXXRecordDecl *NamingClass, DeclAccessPair Found, bool Diagnose) { if (!getLangOpts().AccessControl || !NamingClass || Found.getAccess() == AS_public) return AR_accessible; AccessTarget Entity(Context, AccessTarget::Member, NamingClass, Found, QualType()); if (Diagnose) Entity.setDiag(diag::err_access) << PlacementRange; return CheckAccess(*this, OpLoc, Entity); } /// Checks access to a member. Sema::AccessResult Sema::CheckMemberAccess(SourceLocation UseLoc, CXXRecordDecl *NamingClass, DeclAccessPair Found) { if (!getLangOpts().AccessControl || !NamingClass || Found.getAccess() == AS_public) return AR_accessible; AccessTarget Entity(Context, AccessTarget::Member, NamingClass, Found, QualType()); return CheckAccess(*this, UseLoc, Entity); } /// Checks implicit access to a member in a structured binding. Sema::AccessResult Sema::CheckStructuredBindingMemberAccess(SourceLocation UseLoc, CXXRecordDecl *DecomposedClass, DeclAccessPair Field) { if (!getLangOpts().AccessControl || Field.getAccess() == AS_public) return AR_accessible; AccessTarget Entity(Context, AccessTarget::Member, DecomposedClass, Field, Context.getRecordType(DecomposedClass)); Entity.setDiag(diag::err_decomp_decl_inaccessible_field); return CheckAccess(*this, UseLoc, Entity); } /// Checks access to an overloaded member operator, including /// conversion operators. Sema::AccessResult Sema::CheckMemberOperatorAccess(SourceLocation OpLoc, Expr *ObjectExpr, Expr *ArgExpr, DeclAccessPair Found) { if (!getLangOpts().AccessControl || Found.getAccess() == AS_public) return AR_accessible; const RecordType *RT = ObjectExpr->getType()->castAs(); CXXRecordDecl *NamingClass = cast(RT->getDecl()); AccessTarget Entity(Context, AccessTarget::Member, NamingClass, Found, ObjectExpr->getType()); Entity.setDiag(diag::err_access) << ObjectExpr->getSourceRange() << (ArgExpr ? ArgExpr->getSourceRange() : SourceRange()); return CheckAccess(*this, OpLoc, Entity); } /// Checks access to the target of a friend declaration. Sema::AccessResult Sema::CheckFriendAccess(NamedDecl *target) { assert(isa(target->getAsFunction())); // Friendship lookup is a redeclaration lookup, so there's never an // inheritance path modifying access. AccessSpecifier access = target->getAccess(); if (!getLangOpts().AccessControl || access == AS_public) return AR_accessible; CXXMethodDecl *method = cast(target->getAsFunction()); AccessTarget entity(Context, AccessTarget::Member, cast(target->getDeclContext()), DeclAccessPair::make(target, access), /*no instance context*/ QualType()); entity.setDiag(diag::err_access_friend_function) << (method->getQualifier() ? method->getQualifierLoc().getSourceRange() : method->getNameInfo().getSourceRange()); // We need to bypass delayed-diagnostics because we might be called // while the ParsingDeclarator is active. EffectiveContext EC(CurContext); switch (CheckEffectiveAccess(*this, EC, target->getLocation(), entity)) { case ::AR_accessible: return Sema::AR_accessible; case ::AR_inaccessible: return Sema::AR_inaccessible; case ::AR_dependent: return Sema::AR_dependent; } llvm_unreachable("invalid access result"); } Sema::AccessResult Sema::CheckAddressOfMemberAccess(Expr *OvlExpr, DeclAccessPair Found) { if (!getLangOpts().AccessControl || Found.getAccess() == AS_none || Found.getAccess() == AS_public) return AR_accessible; OverloadExpr *Ovl = OverloadExpr::find(OvlExpr).Expression; CXXRecordDecl *NamingClass = Ovl->getNamingClass(); AccessTarget Entity(Context, AccessTarget::Member, NamingClass, Found, /*no instance context*/ QualType()); Entity.setDiag(diag::err_access) << Ovl->getSourceRange(); return CheckAccess(*this, Ovl->getNameLoc(), Entity); } /// Checks access for a hierarchy conversion. /// /// \param ForceCheck true if this check should be performed even if access /// control is disabled; some things rely on this for semantics /// \param ForceUnprivileged true if this check should proceed as if the /// context had no special privileges Sema::AccessResult Sema::CheckBaseClassAccess(SourceLocation AccessLoc, QualType Base, QualType Derived, const CXXBasePath &Path, unsigned DiagID, bool ForceCheck, bool ForceUnprivileged) { if (!ForceCheck && !getLangOpts().AccessControl) return AR_accessible; if (Path.Access == AS_public) return AR_accessible; CXXRecordDecl *BaseD, *DerivedD; BaseD = cast(Base->castAs()->getDecl()); DerivedD = cast(Derived->castAs()->getDecl()); AccessTarget Entity(Context, AccessTarget::Base, BaseD, DerivedD, Path.Access); if (DiagID) Entity.setDiag(DiagID) << Derived << Base; if (ForceUnprivileged) { switch (CheckEffectiveAccess(*this, EffectiveContext(), AccessLoc, Entity)) { case ::AR_accessible: return Sema::AR_accessible; case ::AR_inaccessible: return Sema::AR_inaccessible; case ::AR_dependent: return Sema::AR_dependent; } llvm_unreachable("unexpected result from CheckEffectiveAccess"); } return CheckAccess(*this, AccessLoc, Entity); } /// Checks access to all the declarations in the given result set. void Sema::CheckLookupAccess(const LookupResult &R) { assert(getLangOpts().AccessControl && "performing access check without access control"); assert(R.getNamingClass() && "performing access check without naming class"); for (LookupResult::iterator I = R.begin(), E = R.end(); I != E; ++I) { if (I.getAccess() != AS_public) { AccessTarget Entity(Context, AccessedEntity::Member, R.getNamingClass(), I.getPair(), R.getBaseObjectType()); Entity.setDiag(diag::err_access); CheckAccess(*this, R.getNameLoc(), Entity); } } } /// Checks access to Target from the given class. The check will take access /// specifiers into account, but no member access expressions and such. /// /// \param Target the declaration to check if it can be accessed /// \param NamingClass the class in which the lookup was started. /// \param BaseType type of the left side of member access expression. /// \p BaseType and \p NamingClass are used for C++ access control. /// Depending on the lookup case, they should be set to the following: /// - lhs.target (member access without a qualifier): /// \p BaseType and \p NamingClass are both the type of 'lhs'. /// - lhs.X::target (member access with a qualifier): /// BaseType is the type of 'lhs', NamingClass is 'X' /// - X::target (qualified lookup without member access): /// BaseType is null, NamingClass is 'X'. /// - target (unqualified lookup). /// BaseType is null, NamingClass is the parent class of 'target'. /// \return true if the Target is accessible from the Class, false otherwise. bool Sema::IsSimplyAccessible(NamedDecl *Target, CXXRecordDecl *NamingClass, QualType BaseType) { // Perform the C++ accessibility checks first. if (Target->isCXXClassMember() && NamingClass) { if (!getLangOpts().CPlusPlus) return false; // The unprivileged access is AS_none as we don't know how the member was // accessed, which is described by the access in DeclAccessPair. // `IsAccessible` will examine the actual access of Target (i.e. // Decl->getAccess()) when calculating the access. AccessTarget Entity(Context, AccessedEntity::Member, NamingClass, DeclAccessPair::make(Target, AS_none), BaseType); EffectiveContext EC(CurContext); return ::IsAccessible(*this, EC, Entity) != ::AR_inaccessible; } if (ObjCIvarDecl *Ivar = dyn_cast(Target)) { // @public and @package ivars are always accessible. if (Ivar->getCanonicalAccessControl() == ObjCIvarDecl::Public || Ivar->getCanonicalAccessControl() == ObjCIvarDecl::Package) return true; // If we are inside a class or category implementation, determine the // interface we're in. ObjCInterfaceDecl *ClassOfMethodDecl = nullptr; if (ObjCMethodDecl *MD = getCurMethodDecl()) ClassOfMethodDecl = MD->getClassInterface(); else if (FunctionDecl *FD = getCurFunctionDecl()) { if (ObjCImplDecl *Impl = dyn_cast(FD->getLexicalDeclContext())) { if (ObjCImplementationDecl *IMPD = dyn_cast(Impl)) ClassOfMethodDecl = IMPD->getClassInterface(); else if (ObjCCategoryImplDecl* CatImplClass = dyn_cast(Impl)) ClassOfMethodDecl = CatImplClass->getClassInterface(); } } // If we're not in an interface, this ivar is inaccessible. if (!ClassOfMethodDecl) return false; // If we're inside the same interface that owns the ivar, we're fine. if (declaresSameEntity(ClassOfMethodDecl, Ivar->getContainingInterface())) return true; // If the ivar is private, it's inaccessible. if (Ivar->getCanonicalAccessControl() == ObjCIvarDecl::Private) return false; return Ivar->getContainingInterface()->isSuperClassOf(ClassOfMethodDecl); } return true; }