From 222e2a7620e6520ffaf4fc4e69d79c18da31542e Mon Sep 17 00:00:00 2001 From: "Zancanaro; Carlo" Date: Mon, 24 Sep 2012 09:58:17 +1000 Subject: Add the clang library to the repo (with some of my changes, too). --- clang/lib/Sema/SemaAccess.cpp | 1850 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1850 insertions(+) create mode 100644 clang/lib/Sema/SemaAccess.cpp (limited to 'clang/lib/Sema/SemaAccess.cpp') diff --git a/clang/lib/Sema/SemaAccess.cpp b/clang/lib/Sema/SemaAccess.cpp new file mode 100644 index 0000000..01c141e --- /dev/null +++ b/clang/lib/Sema/SemaAccess.cpp @@ -0,0 +1,1850 @@ +//===---- 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/Sema/SemaInternal.h" +#include "clang/Sema/DelayedDiagnostic.h" +#include "clang/Sema/Initialization.h" +#include "clang/Sema/Lookup.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" + +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(0), Dependent(false) {} + + explicit EffectiveContext(DeclContext *DC) + : Inner(DC), + Dependent(DC->isDependentContext()) { + + // C++ [class.access.nest]p1: + // A nested class is a member and as such has the same access + // rights as any other member. + // C++ [class.access]p2: + // A member of a class can also access all the names to which + // the class has access. 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) { + if (isa(DC)) { + CXXRecordDecl *Record = cast(DC)->getCanonicalDecl(); + Records.push_back(Record); + DC = Record->getDeclContext(); + } else if (isa(DC)) { + FunctionDecl *Function = cast(DC)->getCanonicalDecl(); + Functions.push_back(Function); + + 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::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, Member, NamingClass, FoundDecl, BaseObjectType) { + initialize(); + } + + AccessTarget(ASTContext &Context, + BaseNonce _, + CXXRecordDecl *BaseClass, + CXXRecordDecl *DerivedClass, + AccessSpecifier Access) + : AccessedEntity(Context, Base, BaseClass, DerivedClass, Access) { + initialize(); + } + + bool isInstanceMember() const { + return (isMemberAccess() && getTargetDecl()->isCXXInstanceMember()); + } + + bool hasInstanceContext() const { + return HasInstanceContext; + } + + class SavedInstanceContext { + public: + ~SavedInstanceContext() { + 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() : 0); + return InstanceContext; + } + + const CXXRecordDecl *getDeclaringClass() const { + return DeclaringClass; + } + +private: + void initialize() { + HasInstanceContext = (isMemberAccess() && + !getBaseObjectType().isNull() && + getTargetDecl()->isCXXInstanceMember()); + CalculatedInstanceContext = false; + InstanceContext = 0; + + 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()) + return AR_dependent; + + for (CXXRecordDecl::base_class_const_iterator + I = Derived->bases_begin(), E = Derived->bases_end(); I != E; ++I) { + + 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.back(); + Queue.pop_back(); + } + + 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->getNumArgs() != ContextTy->getNumArgs()) + return false; + + if (!MightInstantiateTo(S, + ContextTy->getResultType(), + FriendTy->getResultType())) + return false; + + for (unsigned I = 0, E = FriendTy->getNumArgs(); I != E; ++I) + if (!MightInstantiateTo(S, + ContextTy->getArgType(I), + FriendTy->getArgType(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()) { + CanQualType FriendTy + = S.Context.getCanonicalType(S.Context.getTypeDeclType(Friend)); + + for (EffectiveContext::record_iterator + I = EC.Records.begin(), E = EC.Records.end(); I != E; ++I) { + CanQualType ContextTy + = S.Context.getCanonicalType(S.Context.getTypeDeclType(*I)); + if (MightInstantiateTo(S, ContextTy, FriendTy)) + 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 (CXXRecordDecl::friend_iterator I = Class->friend_begin(), + E = Class->friend_end(); I != E; ++I) { + FriendDecl *Friend = *I; + + 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 (CXXRecordDecl::base_class_const_iterator + I = Cur->bases_begin(), E = Cur->bases_end(); I != E; ++I) { + + // 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 == 0 || + 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 + // a static function member functions. + if (S.getLangOpts().MicrosoftMode && !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 = 0; + 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 = 0; + + 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 == 0 || 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 0; + + 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.getNamingClass(); + NamingClass = NamingClass->getCanonicalDecl(); + + 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()))) { + S.Diag(D->getLocation(), diag::note_access_protected_restricted_ctordtor) + << isa(D); + return true; + } + + // Otherwise, use the generic diagnostic. + S.Diag(D->getLocation(), diag::note_access_protected_restricted_object) + << S.Context.getTypeDeclType(ECRecord); + return true; + } + + return false; +} + +/// Diagnose the path which caused the given declaration or base class +/// to become inaccessible. +static void DiagnoseAccessPath(Sema &S, + const EffectiveContext &EC, + AccessTarget &Entity) { + AccessSpecifier Access = Entity.getAccess(); + + NamedDecl *D = (Entity.isMemberAccess() ? Entity.getTargetDecl() : 0); + const CXXRecordDecl *DeclaringClass = Entity.getDeclaringClass(); + + // Easy case: the decl's natural access determined its path access. + // We have to check against AS_private here in case Access is AS_none, + // indicating a non-public member of a private base class. + if (D && (Access == D->getAccess() || D->getAccess() == AS_private)) { + switch (HasAccess(S, EC, DeclaringClass, D->getAccess(), Entity)) { + case AR_inaccessible: { + if (Access == AS_protected && + TryDiagnoseProtectedAccess(S, EC, Entity)) + return; + + // Find an original declaration. + while (D->isOutOfLine()) { + NamedDecl *PrevDecl = 0; + 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 Implicit = true; + for (CXXRecordDecl::decl_iterator + I = DeclaringClass->decls_begin(), E = DeclaringClass->decls_end(); + I != E; ++I) { + if (*I == ImmediateChild) break; + if (isa(*I)) { + Implicit = false; + break; + } + } + + S.Diag(D->getLocation(), diag::note_access_natural) + << (unsigned) (Access == AS_protected) + << Implicit; + return; + } + + case AR_accessible: break; + + case AR_dependent: + llvm_unreachable("can't diagnose dependent access failures"); + } + } + + CXXBasePaths Paths; + CXXBasePath &Path = *FindBestPath(S, EC, Entity, AS_public, Paths); + + CXXBasePath::iterator I = Path.end(), E = Path.begin(); + while (I != E) { + --I; + + const CXXBaseSpecifier *BS = I->Base; + AccessSpecifier BaseAccess = BS->getAccessSpecifier(); + + // If this is public inheritance, or the derived class is a friend, + // skip this step. + if (BaseAccess == AS_public) + continue; + + switch (GetFriendKind(S, EC, I->Class)) { + case AR_accessible: continue; + case AR_inaccessible: break; + case AR_dependent: + llvm_unreachable("can't diagnose dependent access failures"); + } + + // Check whether this base specifier is the tighest point + // constraining access. We have to check against AS_private for + // the same reasons as above. + if (BaseAccess == AS_private || BaseAccess >= Access) { + + // We're constrained by inheritance, but we want to say + // "declared private here" if we're diagnosing a hierarchy + // conversion and this is the final step. + unsigned diagnostic; + if (D) diagnostic = diag::note_access_constrained_by_path; + else if (I + 1 == Path.end()) diagnostic = diag::note_access_natural; + else diagnostic = diag::note_access_constrained_by_path; + + S.Diag(BS->getSourceRange().getBegin(), diagnostic) + << BS->getSourceRange() + << (BaseAccess == AS_protected) + << (BS->getAccessSpecifierAsWritten() == AS_none); + + if (D) + S.Diag(D->getLocation(), diag::note_field_decl); + + return; + } + } + + llvm_unreachable("access not apparently constrained by path"); +} + +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() : 0); + + 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. + CXXRecordDecl *NamingClass = Entity.getNamingClass(); + while (NamingClass->isAnonymousStructOrUnion()) + NamingClass = cast(NamingClass->getParent()); + NamingClass = NamingClass->getCanonicalDecl(); + + AccessSpecifier UnprivilegedAccess = Entity.getAccess(); + assert(UnprivilegedAccess != AS_public && "public access not weeded out"); + + // Before we try to recalculate access paths, try to white-list + // accesses which just trade in on the final step, i.e. accesses + // which don't require [M4] or [B4]. These are by far the most + // common forms of privileged access. + if (UnprivilegedAccess != AS_none) { + switch (HasAccess(S, EC, NamingClass, UnprivilegedAccess, Entity)) { + case AR_dependent: + // This is actually an interesting policy decision. We don't + // *have* to delay immediately here: we can do the full access + // calculation in the hope that friendship on some intermediate + // class will make the declaration accessible non-dependently. + // But that's not cheap, and odds are very good (note: assertion + // made without data) that the friend declaration will determine + // access. + return AR_dependent; + + case AR_accessible: return AR_accessible; + case AR_inaccessible: break; + } + } + + AccessTarget::SavedInstanceContext _ = Entity.saveInstanceContext(); + + // We lower member accesses to base accesses by pretending that the + // member is a base class of its declaring class. + AccessSpecifier FinalAccess; + + if (Entity.isMemberAccess()) { + // Determine if the declaration is accessible from EC when named + // in its declaring class. + NamedDecl *Target = Entity.getTargetDecl(); + const CXXRecordDecl *DeclaringClass = Entity.getDeclaringClass(); + + FinalAccess = Target->getAccess(); + switch (HasAccess(S, EC, DeclaringClass, FinalAccess, Entity)) { + case AR_accessible: + FinalAccess = AS_public; + break; + case AR_inaccessible: break; + case AR_dependent: return AR_dependent; // see above + } + + if (DeclaringClass == NamingClass) + return (FinalAccess == AS_public ? AR_accessible : AR_inaccessible); + + Entity.suppressInstanceContext(); + } else { + FinalAccess = AS_public; + } + + assert(Entity.getDeclaringClass() != NamingClass); + + // Append the declaration's access if applicable. + CXXBasePaths Paths; + CXXBasePath *Path = FindBestPath(S, EC, Entity, FinalAccess, Paths); + if (!Path) + return AR_dependent; + + assert(Path->Access <= UnprivilegedAccess && + "access along best path worse than direct?"); + if (Path->Access == AS_public) + return AR_accessible; + return AR_inaccessible; +} + +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!"); + + if (S.getLangOpts().MicrosoftMode && + IsMicrosoftUsingDeclarationAccessBug(S, Loc, Entity)) + return AR_accessible; + + switch (IsAccessible(S, EC, Entity)) { + case AR_dependent: + DelayDependentAccess(S, EC, Loc, Entity); + return AR_dependent; + + case AR_inaccessible: + 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 (S.SuppressAccessChecking) + 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("falling off end"); +} + +void Sema::HandleDelayedAccessCheck(DelayedDiagnostic &DD, Decl *decl) { + // 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 = decl->getDeclContext(); + if (FunctionDecl *fn = dyn_cast(decl)) { + if (!DC->isFunctionOrMethod()) DC = fn; + } else if (FunctionTemplateDecl *fnt = dyn_cast(decl)) { + // Never a local declaration. + DC = fnt->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->getAs()->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, + const InitializedEntity &Entity, + AccessSpecifier Access, + bool IsCopyBindingRefToTemp) { + if (!getLangOpts().AccessControl || Access == 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: { + const VarDecl *Var = Entity.getCapturedVar(); + PD = PDiag(diag::err_access_lambda_capture); + PD << Var->getName() << Entity.getType() << getSpecialMember(Constructor); + break; + } + + } + + return CheckConstructorAccess(UseLoc, Constructor, Entity, Access, PD); +} + +/// Checks access to a constructor. +Sema::AccessResult Sema::CheckConstructorAccess(SourceLocation UseLoc, + CXXConstructorDecl *Constructor, + const InitializedEntity &Entity, + AccessSpecifier Access, + const PartialDiagnostic &PD) { + if (!getLangOpts().AccessControl || + Access == 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. + CXXRecordDecl *ObjectClass; + if (Entity.getKind() == InitializedEntity::EK_Base) { + ObjectClass = cast(CurContext)->getParent(); + } else { + ObjectClass = NamingClass; + } + + AccessTarget AccessEntity(Context, AccessTarget::Member, NamingClass, + DeclAccessPair::make(Constructor, Access), + Context.getTypeDeclType(ObjectClass)); + AccessEntity.setDiag(PD); + + return CheckAccess(*this, UseLoc, AccessEntity); +} + +/// Checks direct (i.e. non-inherited) access to an arbitrary class +/// member. +Sema::AccessResult Sema::CheckDirectMemberAccess(SourceLocation UseLoc, + NamedDecl *Target, + const PartialDiagnostic &Diag) { + AccessSpecifier Access = Target->getAccess(); + if (!getLangOpts().AccessControl || + Access == AS_public) + return AR_accessible; + + CXXRecordDecl *NamingClass = cast(Target->getDeclContext()); + AccessTarget Entity(Context, AccessTarget::Member, NamingClass, + DeclAccessPair::make(Target, Access), + QualType()); + Entity.setDiag(Diag); + return CheckAccess(*this, UseLoc, Entity); +} + + +/// 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 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); +} + +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 IsBaseToDerived whether this is a base-to-derived conversion (true) +/// or a derived-to-base conversion (false) +/// \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 +/// \param ADK controls the kind of diagnostics that are used +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->getAs()->getDecl()); + DerivedD = cast(Derived->getAs()->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 Decl from the given class. The check will take access +/// specifiers into account, but no member access expressions and such. +/// +/// \param Decl the declaration to check if it can be accessed +/// \param Class the class/context from which to start the search +/// \return true if the Decl is accessible from the Class, false otherwise. +bool Sema::IsSimplyAccessible(NamedDecl *Decl, DeclContext *Ctx) { + if (CXXRecordDecl *Class = dyn_cast(Ctx)) { + if (!Decl->isCXXClassMember()) + return true; + + QualType qType = Class->getTypeForDecl()->getCanonicalTypeInternal(); + AccessTarget Entity(Context, AccessedEntity::Member, Class, + DeclAccessPair::make(Decl, Decl->getAccess()), + qType); + if (Entity.getAccess() == AS_public) + return true; + + EffectiveContext EC(CurContext); + return ::IsAccessible(*this, EC, Entity) != ::AR_inaccessible; + } + + if (ObjCIvarDecl *Ivar = dyn_cast(Decl)) { + // @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 = 0; + 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; +} + +void Sema::ActOnStartSuppressingAccessChecks() { + assert(!SuppressAccessChecking && + "Tried to start access check suppression when already started."); + SuppressAccessChecking = true; +} + +void Sema::ActOnStopSuppressingAccessChecks() { + assert(SuppressAccessChecking && + "Tried to stop access check suprression when already stopped."); + SuppressAccessChecking = false; +} -- cgit v1.2.3