diff options
Diffstat (limited to 'contrib/llvm/tools/clang/lib/Sema/SemaAccess.cpp')
-rw-r--r-- | contrib/llvm/tools/clang/lib/Sema/SemaAccess.cpp | 1329 |
1 files changed, 1329 insertions, 0 deletions
diff --git a/contrib/llvm/tools/clang/lib/Sema/SemaAccess.cpp b/contrib/llvm/tools/clang/lib/Sema/SemaAccess.cpp new file mode 100644 index 0000000..444ee79 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Sema/SemaAccess.cpp @@ -0,0 +1,1329 @@ +//===---- 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 "Sema.h" +#include "SemaInit.h" +#include "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/DependentDiagnostic.h" +#include "clang/AST/ExprCXX.h" + +using namespace clang; + +/// 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(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<CXXRecordDecl>(DC)) { + CXXRecordDecl *Record = cast<CXXRecordDecl>(DC)->getCanonicalDecl(); + Records.push_back(Record); + DC = Record->getDeclContext(); + } else if (isa<FunctionDecl>(DC)) { + FunctionDecl *Function = cast<FunctionDecl>(DC)->getCanonicalDecl(); + Functions.push_back(Function); + 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 llvm::SmallVectorImpl<CXXRecordDecl*>::const_iterator record_iterator; + + DeclContext *Inner; + llvm::SmallVector<FunctionDecl*, 4> Functions; + llvm::SmallVector<CXXRecordDecl*, 4> Records; + bool Dependent; +}; + +/// Like Sema's AccessedEntity, but kindly lets us scribble all over +/// it. +struct AccessTarget : public Sema::AccessedEntity { + AccessTarget(const Sema::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 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<CXXRecordDecl>(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; + llvm::SmallVector<const CXXRecordDecl*, 8> Queue; // actually a stack + + while (true) { + 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<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.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<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->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<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 (llvm::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. This isn't true in C++0x because of template aliases. + if (!S.LangOpts.CPlusPlus0x && 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 (llvm::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 (llvm::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) { + 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 (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; +} + +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; + } + + if (!Target.hasInstanceContext()) + return AR_accessible; + + const CXXRecordDecl *InstanceContext = Target.resolveInstanceContext(S); + if (!InstanceContext) { + 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]. Most of the + // conditions are encoded by not having any instance context. + switch (IsDerivedFromInclusive(InstanceContext, ECRecord)) { + case AR_accessible: return AR_accessible; + case AR_inaccessible: continue; + case AR_dependent: OnFailure = AR_dependent; continue; + } + } + } + + if (!NamingClass->hasFriends()) + return OnFailure; + + // Don't consider friends if we're under the [class.protected] + // restriction, above. + if (Access == AS_protected && Target.hasInstanceContext()) { + const CXXRecordDecl *InstanceContext = Target.resolveInstanceContext(S); + if (!InstanceContext) return AR_dependent; + + switch (IsDerivedFromInclusive(InstanceContext, NamingClass)) { + case AR_accessible: break; + case AR_inaccessible: return OnFailure; + case AR_dependent: return AR_dependent; + } + } + + 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"); + return OnFailure; +} + +/// 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 = 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; +} + +/// Diagnose the path which caused the given declaration or base class +/// to become inaccessible. +static void DiagnoseAccessPath(Sema &S, + const EffectiveContext &EC, + AccessTarget &Entity) { + AccessSpecifier Access = Entity.getAccess(); + const CXXRecordDecl *NamingClass = Entity.getNamingClass(); + NamingClass = NamingClass->getCanonicalDecl(); + + NamedDecl *D = (Entity.isMemberAccess() ? Entity.getTargetDecl() : 0); + const CXXRecordDecl *DeclaringClass = Entity.getDeclaringClass(); + + // Easy case: the decl's natural access determined its path access. + // We have to check against AS_private here in case Access is AS_none, + // indicating a non-public member of a private base class. + if (D && (Access == D->getAccess() || D->getAccess() == AS_private)) { + switch (HasAccess(S, EC, DeclaringClass, D->getAccess(), Entity)) { + case AR_inaccessible: { + S.Diag(D->getLocation(), diag::note_access_natural) + << (unsigned) (Access == AS_protected) + << /*FIXME: not implicitly*/ 0; + return; + } + + case AR_accessible: break; + + case AR_dependent: + llvm_unreachable("can't diagnose dependent access failures"); + return; + } + } + + CXXBasePaths Paths; + CXXBasePath &Path = *FindBestPath(S, EC, Entity, AS_public, Paths); + + CXXBasePath::iterator I = Path.end(), E = Path.begin(); + while (I != E) { + --I; + + const CXXBaseSpecifier *BS = I->Base; + AccessSpecifier BaseAccess = BS->getAccessSpecifier(); + + // If this is public inheritance, or the derived class is a friend, + // skip this step. + if (BaseAccess == AS_public) + continue; + + switch (GetFriendKind(S, EC, I->Class)) { + case AR_accessible: continue; + case AR_inaccessible: break; + case AR_dependent: + llvm_unreachable("can't diagnose dependent access failures"); + } + + // Check whether this base specifier is the tighest point + // constraining access. We have to check against AS_private for + // the same reasons as above. + if (BaseAccess == AS_private || BaseAccess >= Access) { + + // We're constrained by inheritance, but we want to say + // "declared private here" if we're diagnosing a hierarchy + // conversion and this is the final step. + unsigned diagnostic; + if (D) diagnostic = diag::note_access_constrained_by_path; + else if (I + 1 == Path.end()) diagnostic = diag::note_access_natural; + else diagnostic = diag::note_access_constrained_by_path; + + S.Diag(BS->getSourceRange().getBegin(), diagnostic) + << BS->getSourceRange() + << (BaseAccess == AS_protected) + << (BS->getAccessSpecifierAsWritten() == AS_none); + return; + } + } + + llvm_unreachable("access not apparently constrained by path"); +} + +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); +} + +/// Determines whether the accessed entity is accessible. Public members +/// have been weeded out by this point. +static AccessResult IsAccessible(Sema &S, + const EffectiveContext &EC, + AccessTarget &Entity) { + // Determine the actual naming class. + CXXRecordDecl *NamingClass = Entity.getNamingClass(); + while (NamingClass->isAnonymousStructOrUnion()) + NamingClass = cast<CXXRecordDecl>(NamingClass->getParent()); + NamingClass = NamingClass->getCanonicalDecl(); + + AccessSpecifier UnprivilegedAccess = Entity.getAccess(); + assert(UnprivilegedAccess != AS_public && "public access not weeded out"); + + // Before we try to recalculate access paths, try to white-list + // accesses which just trade in on the final step, i.e. accesses + // which don't require [M4] or [B4]. These are by far the most + // common forms of privileged access. + if (UnprivilegedAccess != AS_none) { + switch (HasAccess(S, EC, NamingClass, UnprivilegedAccess, Entity)) { + case AR_dependent: + // This is actually an interesting policy decision. We don't + // *have* to delay immediately here: we can do the full access + // calculation in the hope that friendship on some intermediate + // class will make the declaration accessible non-dependently. + // But that's not cheap, and odds are very good (note: assertion + // made without data) that the friend declaration will determine + // access. + return AR_dependent; + + case AR_accessible: return AR_accessible; + case AR_inaccessible: break; + } + } + + AccessTarget::SavedInstanceContext _ = Entity.saveInstanceContext(); + + // We lower member accesses to base accesses by pretending that the + // member is a base class of its declaring class. + AccessSpecifier FinalAccess; + + if (Entity.isMemberAccess()) { + // Determine if the declaration is accessible from EC when named + // in its declaring class. + NamedDecl *Target = Entity.getTargetDecl(); + const CXXRecordDecl *DeclaringClass = Entity.getDeclaringClass(); + + FinalAccess = Target->getAccess(); + switch (HasAccess(S, EC, DeclaringClass, FinalAccess, Entity)) { + case AR_accessible: + FinalAccess = AS_public; + break; + case AR_inaccessible: break; + case AR_dependent: return AR_dependent; // see above + } + + if (DeclaringClass == NamingClass) + return (FinalAccess == AS_public ? AR_accessible : AR_inaccessible); + + Entity.suppressInstanceContext(); + } else { + FinalAccess = AS_public; + } + + assert(Entity.getDeclaringClass() != NamingClass); + + // Append the declaration's access if applicable. + CXXBasePaths Paths; + CXXBasePath *Path = FindBestPath(S, EC, Entity, FinalAccess, Paths); + if (!Path) + return AR_dependent; + + assert(Path->Access <= UnprivilegedAccess && + "access along best path worse than direct?"); + if (Path->Access == AS_public) + return AR_accessible; + return AR_inaccessible; +} + +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 (!Entity.isQuiet()) + DiagnoseBadAccess(S, Loc, EC, Entity); + return AR_inaccessible; + + case AR_accessible: + return AR_accessible; + } + + // silence unnecessary warning + llvm_unreachable("invalid access result"); + return AR_accessible; +} + +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 top-level declaration, delay + // diagnostics. This is the only case where parsing a declaration + // can actually change our effective context for the purposes of + // access control. + if (S.CurContext->isFileContext() && S.ParsingDeclDepth) { + S.DelayedDiagnostics.push_back( + Sema::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"); + return Sema::AR_accessible; +} + +void Sema::HandleDelayedAccessCheck(DelayedDiagnostic &DD, Decl *Ctx) { + // Pretend we did this from the context of the newly-parsed + // declaration. If that declaration itself forms a declaration context, + // include it in the effective context so that parameters and return types of + // befriended functions have that function's access priveledges. + DeclContext *DC = Ctx->getDeclContext(); + if (isa<FunctionDecl>(Ctx)) + DC = cast<DeclContext>(Ctx); + else if (FunctionTemplateDecl *FnTpl = dyn_cast<FunctionTemplateDecl>(Ctx)) + DC = cast<DeclContext>(FnTpl->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 (!getLangOptions().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 (!getLangOptions().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); +} + +Sema::AccessResult Sema::CheckDestructorAccess(SourceLocation Loc, + CXXDestructorDecl *Dtor, + const PartialDiagnostic &PDiag) { + if (!getLangOptions().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(); + AccessTarget Entity(Context, AccessTarget::Member, NamingClass, + DeclAccessPair::make(Dtor, Access), + QualType()); + 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) { + if (!getLangOptions().AccessControl || + Access == AS_public) + return AR_accessible; + + CXXRecordDecl *NamingClass = Constructor->getParent(); + AccessTarget AccessEntity(Context, AccessTarget::Member, NamingClass, + DeclAccessPair::make(Constructor, Access), + QualType()); + switch (Entity.getKind()) { + default: + AccessEntity.setDiag(diag::err_access_ctor); + break; + + case InitializedEntity::EK_Base: + AccessEntity.setDiag(PDiag(diag::err_access_base) + << Entity.isInheritedVirtualBase() + << Entity.getBaseSpecifier()->getType() + << getSpecialMember(Constructor)); + break; + + case InitializedEntity::EK_Member: { + const FieldDecl *Field = cast<FieldDecl>(Entity.getDecl()); + AccessEntity.setDiag(PDiag(diag::err_access_field) + << Field->getType() + << getSpecialMember(Constructor)); + break; + } + + } + + 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 (!getLangOptions().AccessControl || + Access == AS_public) + return AR_accessible; + + CXXRecordDecl *NamingClass = cast<CXXRecordDecl>(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) { + if (!getLangOptions().AccessControl || + !NamingClass || + Found.getAccess() == AS_public) + return AR_accessible; + + AccessTarget Entity(Context, AccessTarget::Member, NamingClass, Found, + QualType()); + 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 (!getLangOptions().AccessControl || + Found.getAccess() == AS_public) + return AR_accessible; + + const RecordType *RT = ObjectExpr->getType()->getAs<RecordType>(); + assert(RT && "found member operator but object expr not of record type"); + 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); +} + +Sema::AccessResult Sema::CheckAddressOfMemberAccess(Expr *OvlExpr, + DeclAccessPair Found) { + if (!getLangOptions().AccessControl || + Found.getAccess() == AS_none || + Found.getAccess() == AS_public) + return AR_accessible; + + OverloadExpr *Ovl = OverloadExpr::find(OvlExpr).getPointer(); + CXXRecordDecl *NamingClass = Ovl->getNamingClass(); + + AccessTarget Entity(Context, AccessTarget::Member, NamingClass, Found, + Context.getTypeDeclType(NamingClass)); + 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 && !getLangOptions().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(getLangOptions().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); + } + } +} |