diff options
Diffstat (limited to 'gnu/llvm/tools/clang/lib/Sema/SemaAccess.cpp')
| -rw-r--r-- | gnu/llvm/tools/clang/lib/Sema/SemaAccess.cpp | 1948 |
1 files changed, 0 insertions, 1948 deletions
diff --git a/gnu/llvm/tools/clang/lib/Sema/SemaAccess.cpp b/gnu/llvm/tools/clang/lib/Sema/SemaAccess.cpp deleted file mode 100644 index 69084589efe..00000000000 --- a/gnu/llvm/tools/clang/lib/Sema/SemaAccess.cpp +++ /dev/null @@ -1,1948 +0,0 @@ -//===---- SemaAccess.cpp - C++ Access Control -------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// 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<EnumDecl>(DC)) - DC = cast<EnumDecl>(DC)->getDeclContext(); - - CXXRecordDecl *DeclaringClass = cast<CXXRecordDecl>(DC); - while (DeclaringClass->isAnonymousStructOrUnion()) - DeclaringClass = cast<CXXRecordDecl>(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<CXXRecordDecl>(DC)) { - CXXRecordDecl *Record = cast<CXXRecordDecl>(DC); - Records.push_back(Record->getCanonicalDecl()); - DC = Record->getDeclContext(); - } else if (isa<FunctionDecl>(DC)) { - FunctionDecl *Function = cast<FunctionDecl>(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 std::find(Records.begin(), Records.end(), 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<CXXRecordDecl*>::const_iterator record_iterator; - - DeclContext *Inner; - SmallVector<FunctionDecl*, 4> Functions; - SmallVector<CXXRecordDecl*, 4> 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<CXXRecordDecl>(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<CXXRecordDecl>(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<const CXXRecordDecl*, 8> 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<RecordType>()) { - RD = cast<CXXRecordDecl>(RT->getDecl()); - } else if (const InjectedClassNameType *IT - = T->getAs<InjectedClassNameType>()) { - 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<FunctionProtoType> FriendTy - = S.Context.getCanonicalType(Friend->getType()) - ->getAs<FunctionProtoType>(); - CanQual<FunctionProtoType> ContextTy - = S.Context.getCanonicalType(Context->getType()) - ->getAs<FunctionProtoType>(); - - // 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<RecordType>()) - return MatchesFriend(S, EC, cast<CXXRecordDecl>(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<CXXRecordDecl*>::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<ClassTemplateSpecializationDecl>(Record)) { - CTD = cast<ClassTemplateSpecializationDecl>(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<FunctionDecl*>::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<FunctionDecl*>::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<NamedDecl>(FriendD->getFriendDecl()->getCanonicalDecl()); - - // FIXME: declarations with dependent or templated scope. - - if (isa<ClassTemplateDecl>(Friend)) - return MatchesFriend(S, EC, cast<ClassTemplateDecl>(Friend)); - - if (isa<FunctionTemplateDecl>(Friend)) - return MatchesFriend(S, EC, cast<FunctionTemplateDecl>(Friend)); - - if (isa<CXXRecordDecl>(Friend)) - return MatchesFriend(S, EC, cast<CXXRecordDecl>(Friend)); - - assert(isa<FunctionDecl>(Friend) && "unknown friend decl kind"); - return MatchesFriend(S, EC, cast<FunctionDecl>(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<const CXXRecordDecl*, 20> 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<RecordType>()) { - RD = cast<CXXRecordDecl>(RT->getDecl()); - } else if (const InjectedClassNameType *IT - = T->getAs<InjectedClassNameType>()) { - 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<CXXMethodDecl>(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<CXXRecordDecl*>(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<CXXConstructorDecl>(D) || isa<CXXDestructorDecl>(D) || - (isa<FunctionTemplateDecl>(D) && - isa<CXXConstructorDecl>( - cast<FunctionTemplateDecl>(D)->getTemplatedDecl()))) { - return S.Diag(D->getLocation(), - diag::note_access_protected_restricted_ctordtor) - << isa<CXXDestructorDecl>(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<VarDecl>(D)) - PrevDecl = VD->getPreviousDecl(); - else if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) - PrevDecl = FD->getPreviousDecl(); - else if (TypedefNameDecl *TND = dyn_cast<TypedefNameDecl>(D)) - PrevDecl = TND->getPreviousDecl(); - else if (TagDecl *TD = dyn_cast<TagDecl>(D)) { - if (isa<RecordDecl>(D) && cast<RecordDecl>(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<Decl>(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<AccessSpecDecl>(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<UsingShadowDecl>(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<FunctionDecl>(D)) { - DC = FN; - } else if (TemplateDecl *TD = dyn_cast<TemplateDecl>(D)) { - DC = cast<DeclContext>(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<CXXRecordDecl>(NamingD); - NamedDecl *TargetDecl = cast<NamedDecl>(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<CXXRecordDecl>(TargetD), - cast<CXXRecordDecl>(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->getAs<PointerType>()->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 special member function accessible for the purposes of -/// deciding whether to define a special member function as deleted? -bool Sema::isSpecialMemberAccessibleForDeletion(CXXMethodDecl *decl, - AccessSpecifier access, - QualType objectType) { - // Fast path. - if (access == AS_public || !getLangOpts().AccessControl) return true; - - AccessTarget entity(Context, AccessTarget::Member, decl->getParent(), - DeclAccessPair::make(decl, access), objectType); - - // Suppress diagnostics. - entity.setDiag(PDiag()); - - switch (CheckAccess(*this, SourceLocation(), 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<FieldDecl>(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<CXXConstructorDecl>(CurContext)->getParent(); - } else if (auto *Shadow = - dyn_cast<ConstructorUsingShadowDecl>(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<RecordType>(); - CXXRecordDecl *NamingClass = cast<CXXRecordDecl>(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<CXXMethodDecl>(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<CXXMethodDecl>(target->getAsFunction()); - - AccessTarget entity(Context, AccessTarget::Member, - cast<CXXRecordDecl>(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<CXXRecordDecl>(Base->getAs<RecordType>()->getDecl()); - DerivedD = cast<CXXRecordDecl>(Derived->getAs<RecordType>()->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<ObjCIvarDecl>(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<ObjCImplDecl>(FD->getLexicalDeclContext())) { - if (ObjCImplementationDecl *IMPD - = dyn_cast<ObjCImplementationDecl>(Impl)) - ClassOfMethodDecl = IMPD->getClassInterface(); - else if (ObjCCategoryImplDecl* CatImplClass - = dyn_cast<ObjCCategoryImplDecl>(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; -} |
