From: John McCall Date: Tue, 6 Apr 2010 21:38:20 +0000 (+0000) Subject: Implement the protected access restriction ([class.protected]), which requires X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=161755a09898c95d21bfff33707da9ca41cd53c5;p=clang Implement the protected access restriction ([class.protected]), which requires that protected members be used on objects of types which derive from the naming class of the lookup. My first N attempts at this were poorly-founded, largely because the standard is very badly worded here. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@100562 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/AST/Decl.h b/include/clang/AST/Decl.h index 2cdb983513..569a94e8a1 100644 --- a/include/clang/AST/Decl.h +++ b/include/clang/AST/Decl.h @@ -196,6 +196,10 @@ public: return DC->isRecord(); } + /// \brief Given that this declaration is a C++ class member, + /// determine whether it's an instance member of its class. + bool isCXXInstanceMember() const; + /// \brief Determine what kind of linkage this entity has. Linkage getLinkage() const; diff --git a/include/clang/AST/DeclAccessPair.h b/include/clang/AST/DeclAccessPair.h new file mode 100644 index 0000000000..7ecd8f8bcd --- /dev/null +++ b/include/clang/AST/DeclAccessPair.h @@ -0,0 +1,72 @@ +//===--- DeclAccessPair.h - A decl bundled with its path access -*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the DeclAccessPair class, which provides an +// efficient representation of a pair of a NamedDecl* and an +// AccessSpecifier. Generally the access specifier gives the +// natural access of a declaration when named in a class, as +// defined in C++ [class.access.base]p1. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_AST_DECLACCESSPAIR_H +#define LLVM_CLANG_AST_DECLACCESSPAIR_H + +#include "clang/Basic/Specifiers.h" + +namespace clang { + +class NamedDecl; + +/// A POD class for pairing a NamedDecl* with an access specifier. +/// Can be put into unions. +class DeclAccessPair { + NamedDecl *Ptr; // we'd use llvm::PointerUnion, but it isn't trivial + + enum { Mask = 0x3 }; + +public: + static DeclAccessPair make(NamedDecl *D, AccessSpecifier AS) { + DeclAccessPair p; + p.set(D, AS); + return p; + } + + NamedDecl *getDecl() const { + return (NamedDecl*) (~Mask & (uintptr_t) Ptr); + } + AccessSpecifier getAccess() const { + return AccessSpecifier(Mask & (uintptr_t) Ptr); + } + + void setDecl(NamedDecl *D) { + set(D, getAccess()); + } + void setAccess(AccessSpecifier AS) { + set(getDecl(), AS); + } + void set(NamedDecl *D, AccessSpecifier AS) { + Ptr = reinterpret_cast(uintptr_t(AS) | + reinterpret_cast(D)); + } + + operator NamedDecl*() const { return getDecl(); } + NamedDecl *operator->() const { return getDecl(); } +}; +} + +// Take a moment to tell SmallVector that DeclAccessPair is POD. +namespace llvm { +template struct isPodLike; +template<> struct isPodLike { + static const bool value = true; +}; +} + +#endif diff --git a/include/clang/AST/DeclCXX.h b/include/clang/AST/DeclCXX.h index 23769af46b..9f110748d5 100644 --- a/include/clang/AST/DeclCXX.h +++ b/include/clang/AST/DeclCXX.h @@ -471,6 +471,11 @@ public: friend_iterator friend_end() const; void pushFriendDecl(FriendDecl *FD); + /// Determines whether this record has any friends. + bool hasFriends() const { + return data().FirstFriend != 0; + } + /// hasConstCopyConstructor - Determines whether this class has a /// copy constructor that accepts a const-qualified argument. bool hasConstCopyConstructor(ASTContext &Context) const; diff --git a/include/clang/AST/DependentDiagnostic.h b/include/clang/AST/DependentDiagnostic.h index 1954a282e8..2bbe5020cb 100644 --- a/include/clang/AST/DependentDiagnostic.h +++ b/include/clang/AST/DependentDiagnostic.h @@ -22,6 +22,7 @@ #include "clang/Basic/SourceLocation.h" #include "clang/AST/DeclBase.h" #include "clang/AST/DeclContextInternals.h" +#include "clang/AST/Type.h" namespace clang { @@ -42,6 +43,7 @@ public: AccessSpecifier AS, NamedDecl *TargetDecl, CXXRecordDecl *NamingClass, + QualType BaseObjectType, const PartialDiagnostic &PDiag) { DependentDiagnostic *DD = Create(Context, Parent, PDiag); DD->AccessData.Loc = Loc.getRawEncoding(); @@ -49,6 +51,7 @@ public: DD->AccessData.Access = AS; DD->AccessData.TargetDecl = TargetDecl; DD->AccessData.NamingClass = NamingClass; + DD->AccessData.BaseObjectType = BaseObjectType.getAsOpaquePtr(); return DD; } @@ -81,6 +84,11 @@ public: return AccessData.NamingClass; } + QualType getAccessBaseObjectType() const { + assert(getKind() == Access); + return QualType::getFromOpaquePtr(AccessData.BaseObjectType); + } + const PartialDiagnostic &getDiagnostic() const { return Diag; } @@ -107,6 +115,7 @@ private: unsigned IsMember : 1; NamedDecl *TargetDecl; CXXRecordDecl *NamingClass; + void *BaseObjectType; } AccessData; }; }; diff --git a/include/clang/AST/Expr.h b/include/clang/AST/Expr.h index a687ee5f01..2b172a726f 100644 --- a/include/clang/AST/Expr.h +++ b/include/clang/AST/Expr.h @@ -17,6 +17,7 @@ #include "clang/AST/APValue.h" #include "clang/AST/Stmt.h" #include "clang/AST/Type.h" +#include "clang/AST/DeclAccessPair.h" #include "llvm/ADT/APSInt.h" #include "llvm/ADT/APFloat.h" #include "llvm/ADT/SmallVector.h" @@ -1274,7 +1275,7 @@ public: class MemberExpr : public Expr { /// Extra data stored in some member expressions. struct MemberNameQualifier : public NameQualifier { - NamedDecl *FoundDecl; + DeclAccessPair FoundDecl; }; /// Base - the expression for the base pointer or structure references. In @@ -1349,7 +1350,7 @@ public: static MemberExpr *Create(ASTContext &C, Expr *base, bool isarrow, NestedNameSpecifier *qual, SourceRange qualrange, - ValueDecl *memberdecl, NamedDecl *founddecl, + ValueDecl *memberdecl, DeclAccessPair founddecl, SourceLocation l, const TemplateArgumentListInfo *targs, QualType ty); @@ -1365,9 +1366,10 @@ public: void setMemberDecl(ValueDecl *D) { MemberDecl = D; } /// \brief Retrieves the declaration found by lookup. - NamedDecl *getFoundDecl() const { + DeclAccessPair getFoundDecl() const { if (!HasQualifierOrFoundDecl) - return getMemberDecl(); + return DeclAccessPair::make(getMemberDecl(), + getMemberDecl()->getAccess()); return getMemberQualifier()->FoundDecl; } diff --git a/include/clang/AST/UnresolvedSet.h b/include/clang/AST/UnresolvedSet.h index 553f04d76a..cad7e61d55 100644 --- a/include/clang/AST/UnresolvedSet.h +++ b/include/clang/AST/UnresolvedSet.h @@ -17,56 +17,7 @@ #include #include "llvm/ADT/SmallVector.h" -#include "clang/Basic/Specifiers.h" - -namespace clang { - -class NamedDecl; - -/// A POD class for pairing a NamedDecl* with an access specifier. -/// Can be put into unions. -class DeclAccessPair { - NamedDecl *Ptr; // we'd use llvm::PointerUnion, but it isn't trivial - - enum { Mask = 0x3 }; - -public: - static DeclAccessPair make(NamedDecl *D, AccessSpecifier AS) { - DeclAccessPair p; - p.set(D, AS); - return p; - } - - NamedDecl *getDecl() const { - return (NamedDecl*) (~Mask & (uintptr_t) Ptr); - } - AccessSpecifier getAccess() const { - return AccessSpecifier(Mask & (uintptr_t) Ptr); - } - - void setDecl(NamedDecl *D) { - set(D, getAccess()); - } - void setAccess(AccessSpecifier AS) { - set(getDecl(), AS); - } - void set(NamedDecl *D, AccessSpecifier AS) { - Ptr = reinterpret_cast(uintptr_t(AS) | - reinterpret_cast(D)); - } - - operator NamedDecl*() const { return getDecl(); } - NamedDecl *operator->() const { return getDecl(); } -}; -} - -// Take a moment to tell SmallVector that this is POD. -namespace llvm { -template struct isPodLike; -template<> struct isPodLike { - static const bool value = true; -}; -} +#include "clang/AST/DeclAccessPair.h" namespace clang { diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index dc9fb59e30..3f2baf7c9c 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -494,6 +494,24 @@ NamedDecl *NamedDecl::getUnderlyingDecl() { } } +bool NamedDecl::isCXXInstanceMember() const { + assert(isCXXClassMember() && + "checking whether non-member is instance member"); + + const NamedDecl *D = this; + if (isa(D)) + D = cast(D)->getTargetDecl(); + + if (isa(D)) + return true; + if (isa(D)) + return cast(D)->isInstance(); + if (isa(D)) + return cast(cast(D) + ->getTemplatedDecl())->isInstance(); + return false; +} + //===----------------------------------------------------------------------===// // DeclaratorDecl Implementation //===----------------------------------------------------------------------===// diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index ae4bc8c801..132245e671 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -496,13 +496,15 @@ MemberExpr *MemberExpr::Create(ASTContext &C, Expr *base, bool isarrow, NestedNameSpecifier *qual, SourceRange qualrange, ValueDecl *memberdecl, - NamedDecl *founddecl, + DeclAccessPair founddecl, SourceLocation l, const TemplateArgumentListInfo *targs, QualType ty) { std::size_t Size = sizeof(MemberExpr); - bool hasQualOrFound = (qual != 0 || founddecl != memberdecl); + bool hasQualOrFound = (qual != 0 || + founddecl.getDecl() != memberdecl || + founddecl.getAccess() != memberdecl->getAccess()); if (hasQualOrFound) Size += sizeof(MemberNameQualifier); diff --git a/lib/Sema/Lookup.h b/lib/Sema/Lookup.h index f310c253ab..73559a246d 100644 --- a/lib/Sema/Lookup.h +++ b/lib/Sema/Lookup.h @@ -282,6 +282,18 @@ public: NamingClass = Record; } + /// \brief Returns the base object type associated with this lookup; + /// important for [class.protected]. Most lookups do not have an + /// associated base object. + QualType getBaseObjectType() const { + return BaseObjectType; + } + + /// \brief Sets the base object type for this lookup. + void setBaseObjectType(QualType T) { + BaseObjectType = T; + } + /// \brief Add a declaration to these results with its natural access. /// Does not test the acceptance criteria. void addDecl(NamedDecl *D) { @@ -550,6 +562,7 @@ private: UnresolvedSet<8> Decls; CXXBasePaths *Paths; CXXRecordDecl *NamingClass; + QualType BaseObjectType; // Parameters. Sema &SemaRef; diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index 0766b1e83d..3f5113ed55 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -315,20 +315,11 @@ public: AccessedEntity(ASTContext &Context, MemberNonce _, CXXRecordDecl *NamingClass, - AccessSpecifier Access, - NamedDecl *Target) - : Access(Access), IsMember(true), - Target(Target), NamingClass(NamingClass), - Diag(0, Context.getDiagAllocator()) { - } - - AccessedEntity(ASTContext &Context, - MemberNonce _, - CXXRecordDecl *NamingClass, - DeclAccessPair FoundDecl) + DeclAccessPair FoundDecl, + QualType BaseObjectType) : Access(FoundDecl.getAccess()), IsMember(true), Target(FoundDecl.getDecl()), NamingClass(NamingClass), - Diag(0, Context.getDiagAllocator()) { + BaseObjectType(BaseObjectType), Diag(0, Context.getDiagAllocator()) { } AccessedEntity(ASTContext &Context, @@ -353,6 +344,10 @@ public: CXXRecordDecl *getBaseClass() const { return cast(Target); } CXXRecordDecl *getDerivedClass() const { return NamingClass; } + /// Retrieves the base object type, important when accessing + /// an instance member. + QualType getBaseObjectType() const { return BaseObjectType; } + /// Sets a diagnostic to be performed. The diagnostic is given /// four (additional) arguments: /// %0 - 0 if the entity was private, 1 if protected @@ -378,6 +373,7 @@ public: bool IsMember; NamedDecl *Target; CXXRecordDecl *NamingClass; + QualType BaseObjectType; PartialDiagnostic Diag; }; @@ -1254,10 +1250,10 @@ public: FunctionDecl *ResolveSingleFunctionTemplateSpecialization(Expr *From); Expr *FixOverloadedFunctionReference(Expr *E, - NamedDecl *FoundDecl, + DeclAccessPair FoundDecl, FunctionDecl *Fn); OwningExprResult FixOverloadedFunctionReference(OwningExprResult, - NamedDecl *FoundDecl, + DeclAccessPair FoundDecl, FunctionDecl *Fn); void AddOverloadedCallCandidates(UnresolvedLookupExpr *ULE, diff --git a/lib/Sema/SemaAccess.cpp b/lib/Sema/SemaAccess.cpp index f628884aab..99a8f9f5bd 100644 --- a/lib/Sema/SemaAccess.cpp +++ b/lib/Sema/SemaAccess.cpp @@ -22,6 +22,13 @@ 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). @@ -51,6 +58,20 @@ bool Sema::SetMemberAccessSpecifier(NamedDecl *MemberDecl, 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) {} @@ -109,22 +130,145 @@ struct EffectiveContext { llvm::SmallVector 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 class 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; +}; + } -static CXXRecordDecl *FindDeclaringClass(NamedDecl *D) { - DeclContext *DC = D->getDeclContext(); +/// 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); - // This can only happen at top: enum decls only "publish" their - // immediate members. - if (isa(DC)) - DC = cast(DC)->getDeclContext(); + if (Derived == Target) return AR_accessible; - CXXRecordDecl *DeclaringClass = cast(DC); - while (DeclaringClass->isAnonymousStructOrUnion()) - DeclaringClass = cast(DeclaringClass->getDeclContext()); - return DeclaringClass; + AccessResult OnFailure = AR_inaccessible; + llvm::SmallVector 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()) { + RD = cast(RT->getDecl()); + } else { + // It's possible for a base class to be the current + // instantiation of some enclosing template, but I'm guessing + // nobody will ever care that we just dependently delay here. + assert(T->isDependentType() && "non-dependent base wasn't a record?"); + OnFailure = AR_dependent; + continue; + } + + RD = RD->getCanonicalDecl(); + if (RD == Target) return AR_accessible; + 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) @@ -204,11 +348,11 @@ static bool MightInstantiateTo(Sema &S, Friend->getTemplatedDecl()); } -static Sema::AccessResult MatchesFriend(Sema &S, - const EffectiveContext &EC, - const CXXRecordDecl *Friend) { +static AccessResult MatchesFriend(Sema &S, + const EffectiveContext &EC, + const CXXRecordDecl *Friend) { if (EC.includesClass(Friend)) - return Sema::AR_accessible; + return AR_accessible; if (EC.isDependent()) { CanQualType FriendTy @@ -219,32 +363,32 @@ static Sema::AccessResult MatchesFriend(Sema &S, CanQualType ContextTy = S.Context.getCanonicalType(S.Context.getTypeDeclType(*I)); if (MightInstantiateTo(S, ContextTy, FriendTy)) - return Sema::AR_dependent; + return AR_dependent; } } - return Sema::AR_inaccessible; + return AR_inaccessible; } -static Sema::AccessResult MatchesFriend(Sema &S, - const EffectiveContext &EC, - CanQualType Friend) { +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 Sema::AR_dependent; + return AR_dependent; - return Sema::AR_inaccessible; + return AR_inaccessible; } /// Determines whether the given friend class template matches /// anything in the effective context. -static Sema::AccessResult MatchesFriend(Sema &S, - const EffectiveContext &EC, - ClassTemplateDecl *Friend) { - Sema::AccessResult OnFailure = Sema::AR_inaccessible; +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. @@ -268,7 +412,7 @@ static Sema::AccessResult MatchesFriend(Sema &S, // It's a match. if (Friend == CTD->getCanonicalDecl()) - return Sema::AR_accessible; + return AR_accessible; // If the context isn't dependent, it can't be a dependent match. if (!EC.isDependent()) @@ -286,7 +430,7 @@ static Sema::AccessResult MatchesFriend(Sema &S, continue; // Otherwise, it's a dependent match. - OnFailure = Sema::AR_dependent; + OnFailure = AR_dependent; } return OnFailure; @@ -294,18 +438,18 @@ static Sema::AccessResult MatchesFriend(Sema &S, /// Determines whether the given friend function matches anything in /// the effective context. -static Sema::AccessResult MatchesFriend(Sema &S, - const EffectiveContext &EC, - FunctionDecl *Friend) { - Sema::AccessResult OnFailure = Sema::AR_inaccessible; +static AccessResult MatchesFriend(Sema &S, + const EffectiveContext &EC, + FunctionDecl *Friend) { + AccessResult OnFailure = AR_inaccessible; for (llvm::SmallVectorImpl::const_iterator I = EC.Functions.begin(), E = EC.Functions.end(); I != E; ++I) { if (Friend == *I) - return Sema::AR_accessible; + return AR_accessible; if (EC.isDependent() && MightInstantiateTo(S, *I, Friend)) - OnFailure = Sema::AR_dependent; + OnFailure = AR_dependent; } return OnFailure; @@ -313,12 +457,12 @@ static Sema::AccessResult MatchesFriend(Sema &S, /// Determines whether the given friend function template matches /// anything in the effective context. -static Sema::AccessResult MatchesFriend(Sema &S, - const EffectiveContext &EC, - FunctionTemplateDecl *Friend) { - if (EC.Functions.empty()) return Sema::AR_inaccessible; +static AccessResult MatchesFriend(Sema &S, + const EffectiveContext &EC, + FunctionTemplateDecl *Friend) { + if (EC.Functions.empty()) return AR_inaccessible; - Sema::AccessResult OnFailure = Sema::AR_inaccessible; + AccessResult OnFailure = AR_inaccessible; for (llvm::SmallVectorImpl::const_iterator I = EC.Functions.begin(), E = EC.Functions.end(); I != E; ++I) { @@ -332,10 +476,10 @@ static Sema::AccessResult MatchesFriend(Sema &S, FTD = FTD->getCanonicalDecl(); if (Friend == FTD) - return Sema::AR_accessible; + return AR_accessible; if (EC.isDependent() && MightInstantiateTo(S, FTD, Friend)) - OnFailure = Sema::AR_dependent; + OnFailure = AR_dependent; } return OnFailure; @@ -343,9 +487,9 @@ static Sema::AccessResult MatchesFriend(Sema &S, /// Determines whether the given friend declaration matches anything /// in the effective context. -static Sema::AccessResult MatchesFriend(Sema &S, - const EffectiveContext &EC, - FriendDecl *FriendD) { +static AccessResult MatchesFriend(Sema &S, + const EffectiveContext &EC, + FriendDecl *FriendD) { if (TypeSourceInfo *T = FriendD->getFriendType()) return MatchesFriend(S, EC, T->getType()->getCanonicalTypeUnqualified()); @@ -367,10 +511,10 @@ static Sema::AccessResult MatchesFriend(Sema &S, return MatchesFriend(S, EC, cast(Friend)); } -static Sema::AccessResult GetFriendKind(Sema &S, - const EffectiveContext &EC, - const CXXRecordDecl *Class) { - Sema::AccessResult OnFailure = Sema::AR_inaccessible; +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(), @@ -378,18 +522,15 @@ static Sema::AccessResult GetFriendKind(Sema &S, FriendDecl *Friend = *I; switch (MatchesFriend(S, EC, Friend)) { - case Sema::AR_accessible: - return Sema::AR_accessible; + case AR_accessible: + return AR_accessible; - case Sema::AR_inaccessible: - break; + case AR_inaccessible: + continue; - case Sema::AR_dependent: - OnFailure = Sema::AR_dependent; + case AR_dependent: + OnFailure = AR_dependent; break; - - case Sema::AR_delayed: - llvm_unreachable("cannot get delayed answer from MatchesFriend"); } } @@ -397,16 +538,19 @@ static Sema::AccessResult GetFriendKind(Sema &S, return OnFailure; } -static Sema::AccessResult HasAccess(Sema &S, - const EffectiveContext &EC, - const CXXRecordDecl *NamingClass, - AccessSpecifier Access) { +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 Sema::AR_accessible; + 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 @@ -414,16 +558,75 @@ static Sema::AccessResult HasAccess(Sema &S, const CXXRecordDecl *ECRecord = *I; // [B2] and [M2] - if (ECRecord == NamingClass) - return Sema::AR_accessible; + if (Access == AS_private) { + if (ECRecord == NamingClass) + return AR_accessible; // [B3] and [M3] - if (Access == AS_protected && - ECRecord->isDerivedFrom(const_cast(NamingClass))) - return Sema::AR_accessible; + } 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; + } + } } - return GetFriendKind(S, EC, NamingClass); + 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, @@ -479,17 +682,21 @@ static Sema::AccessResult HasAccess(Sema &S, /// /// B is an accessible base of N at R iff ACAB(1) = public. /// -/// \param FinalAccess the access of the "final step", or AS_none if +/// \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, - CXXRecordDecl *Derived, - CXXRecordDecl *Base, + AccessTarget &Target, AccessSpecifier FinalAccess, CXXBasePaths &Paths) { // Derive the paths to the desired base. - bool isDerived = Derived->isDerivedFrom(Base, Paths); + 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; @@ -502,6 +709,7 @@ static CXXBasePath *FindBestPath(Sema &S, // 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; @@ -519,16 +727,23 @@ static CXXBasePath *FindBestPath(Sema &S, break; } + const CXXRecordDecl *NC = I->Class->getCanonicalDecl(); + AccessSpecifier BaseAccess = I->Base->getAccessSpecifier(); PathAccess = std::max(PathAccess, BaseAccess); - switch (HasAccess(S, EC, I->Class, PathAccess)) { - case Sema::AR_inaccessible: break; - case Sema::AR_accessible: PathAccess = AS_public; break; - case Sema::AR_dependent: + + 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; - case Sema::AR_delayed: - llvm_unreachable("friend resolution is never delayed"); break; } } @@ -561,46 +776,36 @@ static CXXBasePath *FindBestPath(Sema &S, /// to become inaccessible. static void DiagnoseAccessPath(Sema &S, const EffectiveContext &EC, - const Sema::AccessedEntity &Entity) { + AccessTarget &Entity) { AccessSpecifier Access = Entity.getAccess(); - CXXRecordDecl *NamingClass = Entity.getNamingClass(); + const CXXRecordDecl *NamingClass = Entity.getNamingClass(); NamingClass = NamingClass->getCanonicalDecl(); - NamedDecl *D; - CXXRecordDecl *DeclaringClass; - if (Entity.isMemberAccess()) { - D = Entity.getTargetDecl(); - DeclaringClass = FindDeclaringClass(D); - } else { - D = 0; - DeclaringClass = Entity.getBaseClass(); - } - DeclaringClass = DeclaringClass->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())) { - case Sema::AR_inaccessible: { + 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 Sema::AR_accessible: break; + case AR_accessible: break; - case Sema::AR_dependent: - case Sema::AR_delayed: - llvm_unreachable("dependent/delayed not allowed"); + case AR_dependent: + llvm_unreachable("can't diagnose dependent access failures"); return; } } CXXBasePaths Paths; - CXXBasePath &Path = *FindBestPath(S, EC, NamingClass, DeclaringClass, - AS_public, Paths); + CXXBasePath &Path = *FindBestPath(S, EC, Entity, AS_public, Paths); CXXBasePath::iterator I = Path.end(), E = Path.begin(); while (I != E) { @@ -615,12 +820,10 @@ static void DiagnoseAccessPath(Sema &S, continue; switch (GetFriendKind(S, EC, I->Class)) { - case Sema::AR_accessible: continue; - case Sema::AR_inaccessible: break; - - case Sema::AR_dependent: - case Sema::AR_delayed: - llvm_unreachable("dependent friendship, should not be diagnosing"); + 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 @@ -649,17 +852,10 @@ static void DiagnoseAccessPath(Sema &S, static void DiagnoseBadAccess(Sema &S, SourceLocation Loc, const EffectiveContext &EC, - const Sema::AccessedEntity &Entity) { + AccessTarget &Entity) { const CXXRecordDecl *NamingClass = Entity.getNamingClass(); - NamedDecl *D; - const CXXRecordDecl *DeclaringClass; - if (Entity.isMemberAccess()) { - D = Entity.getTargetDecl(); - DeclaringClass = FindDeclaringClass(D); - } else { - D = 0; - DeclaringClass = Entity.getBaseClass(); - } + const CXXRecordDecl *DeclaringClass = Entity.getDeclaringClass(); + NamedDecl *D = (Entity.isMemberAccess() ? Entity.getTargetDecl() : 0); S.Diag(Loc, Entity.getDiag()) << (Entity.getAccess() == AS_protected) @@ -671,9 +867,9 @@ static void DiagnoseBadAccess(Sema &S, SourceLocation Loc, /// Determines whether the accessed entity is accessible. Public members /// have been weeded out by this point. -static Sema::AccessResult IsAccessible(Sema &S, - const EffectiveContext &EC, - const Sema::AccessedEntity &Entity) { +static AccessResult IsAccessible(Sema &S, + const EffectiveContext &EC, + AccessTarget &Entity) { // Determine the actual naming class. CXXRecordDecl *NamingClass = Entity.getNamingClass(); while (NamingClass->isAnonymousStructOrUnion()) @@ -686,10 +882,10 @@ static Sema::AccessResult IsAccessible(Sema &S, // 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 access. + // common forms of privileged access. if (UnprivilegedAccess != AS_none) { - switch (HasAccess(S, EC, NamingClass, UnprivilegedAccess)) { - case Sema::AR_dependent: + 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 @@ -697,23 +893,14 @@ static Sema::AccessResult IsAccessible(Sema &S, // But that's not cheap, and odds are very good (note: assertion // made without data) that the friend declaration will determine // access. - return Sema::AR_dependent; + return AR_dependent; - case Sema::AR_accessible: return Sema::AR_accessible; - case Sema::AR_inaccessible: break; - case Sema::AR_delayed: - llvm_unreachable("friendship never subject to contextual delay"); + case AR_accessible: return AR_accessible; + case AR_inaccessible: break; } } - // Determine the declaring class. - CXXRecordDecl *DeclaringClass; - if (Entity.isMemberAccess()) { - DeclaringClass = FindDeclaringClass(Entity.getTargetDecl()); - } else { - DeclaringClass = Entity.getBaseClass(); - } - DeclaringClass = DeclaringClass->getCanonicalDecl(); + AccessTarget::SavedInstanceContext _ = Entity.saveInstanceContext(); // We lower member accesses to base accesses by pretending that the // member is a base class of its declaring class. @@ -723,43 +910,44 @@ static Sema::AccessResult IsAccessible(Sema &S, // 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)) { - case Sema::AR_accessible: FinalAccess = AS_public; break; - case Sema::AR_inaccessible: break; - case Sema::AR_dependent: return Sema::AR_dependent; // see above - case Sema::AR_delayed: llvm_unreachable("friend status is never delayed"); + 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 - ? Sema::AR_accessible - : Sema::AR_inaccessible); + return (FinalAccess == AS_public ? AR_accessible : AR_inaccessible); + + Entity.suppressInstanceContext(); } else { FinalAccess = AS_public; } - assert(DeclaringClass != NamingClass); + assert(Entity.getDeclaringClass() != NamingClass); // Append the declaration's access if applicable. CXXBasePaths Paths; - CXXBasePath *Path = FindBestPath(S, EC, NamingClass, DeclaringClass, - FinalAccess, Paths); + CXXBasePath *Path = FindBestPath(S, EC, Entity, FinalAccess, Paths); if (!Path) - return Sema::AR_dependent; + return AR_dependent; assert(Path->Access <= UnprivilegedAccess && "access along best path worse than direct?"); if (Path->Access == AS_public) - return Sema::AR_accessible; - return Sema::AR_inaccessible; + return AR_accessible; + return AR_inaccessible; } -static void DelayAccess(Sema &S, - const EffectiveContext &EC, - SourceLocation Loc, - const Sema::AccessedEntity &Entity) { +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"); @@ -769,48 +957,38 @@ static void DelayAccess(Sema &S, Entity.getAccess(), Entity.getTargetDecl(), Entity.getNamingClass(), + Entity.getBaseObjectType(), Entity.getDiag()); } /// Checks access to an entity from the given effective context. -static Sema::AccessResult CheckEffectiveAccess(Sema &S, - const EffectiveContext &EC, - SourceLocation Loc, - const Sema::AccessedEntity &Entity) { +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 Sema::AR_dependent: - DelayAccess(S, EC, Loc, Entity); - return Sema::AR_dependent; + case AR_dependent: + DelayDependentAccess(S, EC, Loc, Entity); + return AR_dependent; - case Sema::AR_delayed: - llvm_unreachable("IsAccessible cannot contextually delay"); - - case Sema::AR_inaccessible: + case AR_inaccessible: if (!Entity.isQuiet()) DiagnoseBadAccess(S, Loc, EC, Entity); - return Sema::AR_inaccessible; - - case Sema::AR_accessible: - break; - } + return AR_inaccessible; - // We only consider the natural access of the declaration when - // deciding whether to do the protected check. - if (Entity.isMemberAccess() && Entity.getAccess() == AS_protected) { - NamedDecl *D = Entity.getTargetDecl(); - if (isa(D) || - (isa(D) && cast(D)->isInstance())) { - // FIXME: implement [class.protected] - } + case AR_accessible: + return AR_accessible; } - return Sema::AR_accessible; + // silence unnecessary warning + llvm_unreachable("invalid access result"); + return AR_accessible; } static Sema::AccessResult CheckAccess(Sema &S, SourceLocation Loc, - const Sema::AccessedEntity &Entity) { + AccessTarget &Entity) { // If the access path is public, it's accessible everywhere. if (Entity.getAccess() == AS_public) return Sema::AR_accessible; @@ -825,8 +1003,14 @@ static Sema::AccessResult CheckAccess(Sema &S, SourceLocation Loc, return Sema::AR_delayed; } - return CheckEffectiveAccess(S, EffectiveContext(S.CurContext), - Loc, Entity); + 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) { @@ -834,7 +1018,9 @@ void Sema::HandleDelayedAccessCheck(DelayedDiagnostic &DD, Decl *Ctx) { // declaration. EffectiveContext EC(Ctx->getDeclContext()); - if (CheckEffectiveAccess(*this, EC, DD.Loc, DD.getAccessData())) + AccessTarget Target(DD.getAccessData()); + + if (CheckEffectiveAccess(*this, EC, DD.Loc, Target) == ::AR_inaccessible) DD.Triggered = true; } @@ -851,19 +1037,28 @@ void Sema::HandleDependentAccessCheck(const DependentDiagnostic &DD, if (!TargetD) return; if (DD.isAccessToMember()) { - AccessedEntity Entity(Context, - AccessedEntity::Member, - cast(NamingD), - Access, - cast(TargetD)); + 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 { - AccessedEntity Entity(Context, - AccessedEntity::Base, - cast(TargetD), - cast(NamingD), - Access); + AccessTarget Entity(Context, + AccessTarget::Base, + cast(TargetD), + cast(NamingD), + Access); Entity.setDiag(DD.getDiagnostic()); CheckAccess(*this, Loc, Entity); } @@ -876,8 +1071,8 @@ Sema::AccessResult Sema::CheckUnresolvedLookupAccess(UnresolvedLookupExpr *E, Found.getAccess() == AS_public) return AR_accessible; - AccessedEntity Entity(Context, AccessedEntity::Member, E->getNamingClass(), - Found); + AccessTarget Entity(Context, AccessTarget::Member, E->getNamingClass(), + Found, QualType()); Entity.setDiag(diag::err_access) << E->getSourceRange(); return CheckAccess(*this, E->getNameLoc(), Entity); @@ -891,8 +1086,12 @@ Sema::AccessResult Sema::CheckUnresolvedMemberAccess(UnresolvedMemberExpr *E, Found.getAccess() == AS_public) return AR_accessible; - AccessedEntity Entity(Context, AccessedEntity::Member, E->getNamingClass(), - Found); + 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); @@ -910,8 +1109,9 @@ Sema::AccessResult Sema::CheckDestructorAccess(SourceLocation Loc, return AR_accessible; CXXRecordDecl *NamingClass = Dtor->getParent(); - AccessedEntity Entity(Context, AccessedEntity::Member, NamingClass, - DeclAccessPair::make(Dtor, Access)); + AccessTarget Entity(Context, AccessTarget::Member, NamingClass, + DeclAccessPair::make(Dtor, Access), + QualType()); Entity.setDiag(PDiag); // TODO: avoid copy return CheckAccess(*this, Loc, Entity); @@ -926,8 +1126,9 @@ Sema::AccessResult Sema::CheckConstructorAccess(SourceLocation UseLoc, return AR_accessible; CXXRecordDecl *NamingClass = Constructor->getParent(); - AccessedEntity Entity(Context, AccessedEntity::Member, NamingClass, - DeclAccessPair::make(Constructor, Access)); + AccessTarget Entity(Context, AccessTarget::Member, NamingClass, + DeclAccessPair::make(Constructor, Access), + QualType()); Entity.setDiag(diag::err_access_ctor); return CheckAccess(*this, UseLoc, Entity); @@ -944,8 +1145,9 @@ Sema::AccessResult Sema::CheckDirectMemberAccess(SourceLocation UseLoc, return AR_accessible; CXXRecordDecl *NamingClass = cast(Target->getDeclContext()); - AccessedEntity Entity(Context, AccessedEntity::Member, NamingClass, - DeclAccessPair::make(Target, Access)); + AccessTarget Entity(Context, AccessTarget::Member, NamingClass, + DeclAccessPair::make(Target, Access), + QualType()); Entity.setDiag(Diag); return CheckAccess(*this, UseLoc, Entity); } @@ -961,7 +1163,8 @@ Sema::AccessResult Sema::CheckAllocationAccess(SourceLocation OpLoc, Found.getAccess() == AS_public) return AR_accessible; - AccessedEntity Entity(Context, AccessedEntity::Member, NamingClass, Found); + AccessTarget Entity(Context, AccessTarget::Member, NamingClass, Found, + QualType()); Entity.setDiag(diag::err_access) << PlacementRange; @@ -982,7 +1185,8 @@ Sema::AccessResult Sema::CheckMemberOperatorAccess(SourceLocation OpLoc, assert(RT && "found member operator but object expr not of record type"); CXXRecordDecl *NamingClass = cast(RT->getDecl()); - AccessedEntity Entity(Context, AccessedEntity::Member, NamingClass, Found); + AccessTarget Entity(Context, AccessTarget::Member, NamingClass, Found, + ObjectExpr->getType()); Entity.setDiag(diag::err_access) << ObjectExpr->getSourceRange() << (ArgExpr ? ArgExpr->getSourceRange() : SourceRange()); @@ -1008,14 +1212,14 @@ Sema::AccessResult Sema::CheckAddressOfMemberAccess(Expr *OvlExpr, assert(DC && DC->isRecord() && "scope did not resolve to record"); CXXRecordDecl *NamingClass = cast(DC); - AccessedEntity Entity(Context, AccessedEntity::Member, NamingClass, Found); + 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) @@ -1042,13 +1246,20 @@ Sema::AccessResult Sema::CheckBaseClassAccess(SourceLocation AccessLoc, BaseD = cast(Base->getAs()->getDecl()); DerivedD = cast(Derived->getAs()->getDecl()); - AccessedEntity Entity(Context, AccessedEntity::Base, BaseD, DerivedD, - Path.Access); + AccessTarget Entity(Context, AccessTarget::Base, BaseD, DerivedD, + Path.Access); if (DiagID) Entity.setDiag(DiagID) << Derived << Base; - if (ForceUnprivileged) - return CheckEffectiveAccess(*this, EffectiveContext(), AccessLoc, Entity); + 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); } @@ -1060,9 +1271,9 @@ void Sema::CheckLookupAccess(const LookupResult &R) { for (LookupResult::iterator I = R.begin(), E = R.end(); I != E; ++I) { if (I.getAccess() != AS_public) { - AccessedEntity Entity(Context, AccessedEntity::Member, - R.getNamingClass(), - I.getPair()); + AccessTarget Entity(Context, AccessedEntity::Member, + R.getNamingClass(), I.getPair(), + R.getBaseObjectType()); Entity.setDiag(diag::err_access); CheckAccess(*this, R.getNameLoc(), Entity); diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index 2dfb95435d..7967a342a2 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -769,24 +769,6 @@ static bool IsProvablyNotDerivedFrom(Sema &SemaRef, return true; } -/// Determines if this is an instance member of a class. -static bool IsInstanceMember(NamedDecl *D) { - assert(D->isCXXClassMember() && - "checking whether non-member is instance member"); - - if (isa(D)) return true; - - if (isa(D)) - return !cast(D)->isStatic(); - - if (isa(D)) { - D = cast(D)->getTemplatedDecl(); - return !cast(D)->isStatic(); - } - - return false; -} - enum IMAKind { /// The reference is definitely not an instance member access. IMA_Static, @@ -846,8 +828,8 @@ static IMAKind ClassifyImplicitMemberAccess(Sema &SemaRef, bool hasNonInstance = false; llvm::SmallPtrSet Classes; for (LookupResult::iterator I = R.begin(), E = R.end(); I != E; ++I) { - NamedDecl *D = (*I)->getUnderlyingDecl(); - if (IsInstanceMember(D)) { + NamedDecl *D = *I; + if (D->isCXXInstanceMember()) { CXXRecordDecl *R = cast(D->getDeclContext()); // If this is a member of an anonymous record, move out to the @@ -1517,8 +1499,8 @@ Sema::PerformObjectMemberConversion(Expr *&From, /// \brief Build a MemberExpr AST node. static MemberExpr *BuildMemberExpr(ASTContext &C, Expr *Base, bool isArrow, const CXXScopeSpec &SS, ValueDecl *Member, - NamedDecl *FoundDecl, SourceLocation Loc, - QualType Ty, + DeclAccessPair FoundDecl, + SourceLocation Loc, QualType Ty, const TemplateArgumentListInfo *TemplateArgs = 0) { NestedNameSpecifier *Qualifier = 0; SourceRange QualifierRange; @@ -2545,7 +2527,7 @@ bool Sema::CheckQualifiedMemberReference(Expr *BaseExpr, for (LookupResult::iterator I = R.begin(), E = R.end(); I != E; ++I) { // If this is an implicit member reference and we find a // non-instance member, it's not an error. - if (!BaseExpr && !IsInstanceMember((*I)->getUnderlyingDecl())) + if (!BaseExpr && !(*I)->isCXXInstanceMember()) return false; // Note that we use the DC of the decl, not the underlying decl. @@ -2682,6 +2664,7 @@ Sema::BuildMemberReferenceExpr(ExprArg Base, QualType BaseExprType, assert(BaseType->isPointerType()); BaseType = BaseType->getAs()->getPointeeType(); } + R.setBaseObjectType(BaseType); NestedNameSpecifier *Qualifier = static_cast(SS.getScopeRep()); @@ -2742,7 +2725,7 @@ Sema::BuildMemberReferenceExpr(ExprArg Base, QualType BaseExprType, } assert(R.isSingleResult()); - NamedDecl *FoundDecl = *R.begin(); + DeclAccessPair FoundDecl = R.begin().getPair(); NamedDecl *MemberDecl = R.getFoundDecl(); // FIXME: diagnose the presence of template arguments now. @@ -2756,7 +2739,7 @@ Sema::BuildMemberReferenceExpr(ExprArg Base, QualType BaseExprType, // Handle the implicit-member-access case. if (!BaseExpr) { // If this is not an instance member, convert to a non-member access. - if (!IsInstanceMember(MemberDecl)) + if (!MemberDecl->isCXXInstanceMember()) return BuildDeclarationNameExpr(SS, R.getNameLoc(), MemberDecl); SourceLocation Loc = R.getNameLoc(); diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index bc10a58d6b..3fabbbbffe 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -6003,7 +6003,7 @@ Sema::BuildCallToMemberFunction(Scope *S, Expr *MemExprE, MemberExpr *MemExpr; CXXMethodDecl *Method = 0; - NamedDecl *FoundDecl = 0; + DeclAccessPair FoundDecl; NestedNameSpecifier *Qualifier = 0; if (isa(NakedMemExpr)) { MemExpr = cast(NakedMemExpr); @@ -6486,7 +6486,7 @@ Sema::BuildOverloadedArrowExpr(Scope *S, ExprArg BaseIn, SourceLocation OpLoc) { /// perhaps a '&' around it). We have resolved the overloaded function /// to the function declaration Fn, so patch up the expression E to /// refer (possibly indirectly) to Fn. Returns the new expr. -Expr *Sema::FixOverloadedFunctionReference(Expr *E, NamedDecl *Found, +Expr *Sema::FixOverloadedFunctionReference(Expr *E, DeclAccessPair Found, FunctionDecl *Fn) { if (ParenExpr *PE = dyn_cast(E)) { Expr *SubExpr = FixOverloadedFunctionReference(PE->getSubExpr(), @@ -6619,7 +6619,7 @@ Expr *Sema::FixOverloadedFunctionReference(Expr *E, NamedDecl *Found, } Sema::OwningExprResult Sema::FixOverloadedFunctionReference(OwningExprResult E, - NamedDecl *Found, + DeclAccessPair Found, FunctionDecl *Fn) { return Owned(FixOverloadedFunctionReference((Expr *)E.get(), Found, Fn)); } diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp index 8278691a4a..cbd12d4380 100644 --- a/lib/Sema/SemaType.cpp +++ b/lib/Sema/SemaType.cpp @@ -2029,7 +2029,10 @@ QualType Sema::BuildTypeofExprType(Expr *E) { // function template specialization wherever deduction cannot occur. if (FunctionDecl *Specialization = ResolveSingleFunctionTemplateSpecialization(E)) { - E = FixOverloadedFunctionReference(E, Specialization, Specialization); + // The access doesn't really matter in this case. + DeclAccessPair Found = DeclAccessPair::make(Specialization, + Specialization->getAccess()); + E = FixOverloadedFunctionReference(E, Found, Specialization); if (!E) return QualType(); } else { @@ -2049,7 +2052,10 @@ QualType Sema::BuildDecltypeType(Expr *E) { // function template specialization wherever deduction cannot occur. if (FunctionDecl *Specialization = ResolveSingleFunctionTemplateSpecialization(E)) { - E = FixOverloadedFunctionReference(E, Specialization, Specialization); + // The access doesn't really matter in this case. + DeclAccessPair Found = DeclAccessPair::make(Specialization, + Specialization->getAccess()); + E = FixOverloadedFunctionReference(E, Found, Specialization); if (!E) return QualType(); } else { diff --git a/test/CXX/class.access/class.protected/p1.cpp b/test/CXX/class.access/class.protected/p1.cpp new file mode 100644 index 0000000000..949475cc66 --- /dev/null +++ b/test/CXX/class.access/class.protected/p1.cpp @@ -0,0 +1,387 @@ +// RUN: %clang_cc1 -fsyntax-only -faccess-control -verify %s + +namespace test0 { + class A { + protected: int x; // expected-note 3 {{declared}} + static int sx; // expected-note 3 {{declared}} + }; + class B : public A { + }; + class C : protected A { // expected-note {{declared}} + }; + class D : private B { // expected-note 3 {{constrained}} + }; + + void test(A &a) { + (void) a.x; // expected-error {{'x' is a protected member}} + (void) a.sx; // expected-error {{'sx' is a protected member}} + } + void test(B &b) { + (void) b.x; // expected-error {{'x' is a protected member}} + (void) b.sx; // expected-error {{'sx' is a protected member}} + } + void test(C &c) { + (void) c.x; // expected-error {{'x' is a protected member}} expected-error {{protected base class}} + (void) c.sx; // expected-error {{'sx' is a protected member}} + } + void test(D &d) { + (void) d.x; // expected-error {{'x' is a private member}} expected-error {{private base class}} + (void) d.sx; // expected-error {{'sx' is a private member}} + } +} + +namespace test1 { + class A { + protected: int x; + static int sx; + static void test(A&); + }; + class B : public A { + static void test(B&); + }; + class C : protected A { + static void test(C&); + }; + class D : private B { + static void test(D&); + }; + + void A::test(A &a) { + (void) a.x; + (void) a.sx; + } + void B::test(B &b) { + (void) b.x; + (void) b.sx; + } + void C::test(C &c) { + (void) c.x; + (void) c.sx; + } + void D::test(D &d) { + (void) d.x; + (void) d.sx; + } +} + +namespace test2 { + class A { + protected: int x; // expected-note 3 {{declared}} + static int sx; + static void test(A&); + }; + class B : public A { + static void test(A&); + }; + class C : protected A { + static void test(A&); + }; + class D : private B { + static void test(A&); + }; + + void A::test(A &a) { + (void) a.x; + (void) a.sx; + } + void B::test(A &a) { + (void) a.x; // expected-error {{'x' is a protected member}} + (void) a.sx; + } + void C::test(A &a) { + (void) a.x; // expected-error {{'x' is a protected member}} + (void) a.sx; + } + void D::test(A &a) { + (void) a.x; // expected-error {{'x' is a protected member}} + (void) a.sx; + } +} + +namespace test3 { + class B; + class A { + protected: int x; // expected-note {{declared}} + static int sx; + static void test(B&); + }; + class B : public A { + static void test(B&); + }; + class C : protected A { + static void test(B&); + }; + class D : private B { + static void test(B&); + }; + + void A::test(B &b) { + (void) b.x; + (void) b.sx; + } + void B::test(B &b) { + (void) b.x; + (void) b.sx; + } + void C::test(B &b) { + (void) b.x; // expected-error {{'x' is a protected member}} + (void) b.sx; + } + void D::test(B &b) { + (void) b.x; + (void) b.sx; + } +} + +namespace test4 { + class C; + class A { + protected: int x; // expected-note 2 {{declared}} + static int sx; + static void test(C&); + }; + class B : public A { + static void test(C&); + }; + class C : protected A { // expected-note 4 {{constrained}} expected-note 3 {{declared}} + static void test(C&); + }; + class D : private B { + static void test(C&); + }; + + void A::test(C &c) { + (void) c.x; // expected-error {{'x' is a protected member}} \ + // expected-error {{protected base class}} + (void) c.sx; // expected-error {{'sx' is a protected member}} + } + void B::test(C &c) { + (void) c.x; // expected-error {{'x' is a protected member}} \ + // expected-error {{protected base class}} + (void) c.sx; // expected-error {{'sx' is a protected member}} + } + void C::test(C &c) { + (void) c.x; + (void) c.sx; + } + void D::test(C &c) { + (void) c.x; // expected-error {{'x' is a protected member}} \ + // expected-error {{protected base class}} + (void) c.sx; // expected-error {{'sx' is a protected member}} + } +} + +namespace test5 { + class D; + class A { + protected: int x; + static int sx; + static void test(D&); + }; + class B : public A { + static void test(D&); + }; + class C : protected A { + static void test(D&); + }; + class D : private B { // expected-note 9 {{constrained}} + static void test(D&); + }; + + void A::test(D &d) { + (void) d.x; // expected-error {{'x' is a private member}} \ + // expected-error {{cannot cast}} + (void) d.sx; // expected-error {{'sx' is a private member}} + } + void B::test(D &d) { + (void) d.x; // expected-error {{'x' is a private member}} \ + // expected-error {{cannot cast}} + (void) d.sx; // expected-error {{'sx' is a private member}} + } + void C::test(D &d) { + (void) d.x; // expected-error {{'x' is a private member}} \ + // expected-error {{cannot cast}} + (void) d.sx; // expected-error {{'sx' is a private member}} + } + void D::test(D &d) { + (void) d.x; + (void) d.sx; + } +} + +namespace test6 { + class Static {}; + class A { + protected: + void foo(int); // expected-note 3 {{declared}} + void foo(long); + static void foo(Static); + + static void test(A&); + }; + class B : public A { + static void test(A&); + }; + class C : protected A { + static void test(A&); + }; + class D : private B { + static void test(A&); + }; + + void A::test(A &a) { + a.foo(10); + a.foo(Static()); + } + void B::test(A &a) { + a.foo(10); // expected-error {{'foo' is a protected member}} + a.foo(Static()); + } + void C::test(A &a) { + a.foo(10); // expected-error {{'foo' is a protected member}} + a.foo(Static()); + } + void D::test(A &a) { + a.foo(10); // expected-error {{'foo' is a protected member}} + a.foo(Static()); + } +} + +namespace test7 { + class Static {}; + class A { + protected: + void foo(int); // expected-note 3 {{declared}} + void foo(long); + static void foo(Static); + + static void test(); + }; + class B : public A { + static void test(); + }; + class C : protected A { + static void test(); + }; + class D : private B { + static void test(); + }; + + void A::test() { + void (A::*x)(int) = &A::foo; + void (*sx)(Static) = &A::foo; + } + void B::test() { + void (A::*x)(int) = &A::foo; // expected-error {{'foo' is a protected member}} + void (*sx)(Static) = &A::foo; + } + void C::test() { + void (A::*x)(int) = &A::foo; // expected-error {{'foo' is a protected member}} + void (*sx)(Static) = &A::foo; + } + void D::test() { + void (A::*x)(int) = &A::foo; // expected-error {{'foo' is a protected member}} + void (*sx)(Static) = &A::foo; + } +} + +namespace test8 { + class Static {}; + class A { + protected: + void foo(int); // expected-note 3 {{declared}} + void foo(long); + static void foo(Static); + + static void test(); + }; + class B : public A { + static void test(); + }; + class C : protected A { + static void test(); + }; + class D : private B { + static void test(); + }; + void call(void (A::*)(int)); + void calls(void (*)(Static)); + + void A::test() { + call(&A::foo); + calls(&A::foo); + } + void B::test() { + call(&A::foo); // expected-error {{'foo' is a protected member}} + calls(&A::foo); + } + void C::test() { + call(&A::foo); // expected-error {{'foo' is a protected member}} + calls(&A::foo); + } + void D::test() { + call(&A::foo); // expected-error {{'foo' is a protected member}} + calls(&A::foo); + } +} + +namespace test9 { + class A { + protected: int foo(); // expected-note 8 {{declared}} + }; + + class B : public A { + friend class D; + }; + + class C : protected B { // expected-note {{declared}} \ + // expected-note 6 {{constrained}} + }; + + class D : public A { + static void test(A &a) { + a.foo(); // expected-error {{'foo' is a protected member}} + a.A::foo(); // expected-error {{'foo' is a protected member}} + a.B::foo(); + a.C::foo(); // expected-error {{'foo' is a protected member}} + } + + static void test(B &b) { + b.foo(); + b.A::foo(); // expected-error {{'foo' is a protected member}} + b.B::foo(); + b.C::foo(); // expected-error {{'foo' is a protected member}} + } + + static void test(C &c) { + c.foo(); // expected-error {{'foo' is a protected member}} \ + // expected-error {{cannot cast}} + c.A::foo(); // expected-error {{'foo' is a protected member}} \ + // expected-error {{'A' is a protected member}} \ + // expected-error {{cannot cast}} + c.B::foo(); // expected-error {{'B' is a protected member}} \ + // expected-error {{cannot cast}} + c.C::foo(); // expected-error {{'foo' is a protected member}} \ + // expected-error {{cannot cast}} + } + + static void test(D &d) { + d.foo(); + d.A::foo(); + d.B::foo(); + d.C::foo(); // expected-error {{'foo' is a protected member}} + } + }; +} + +namespace test10 { + template class A { + protected: + int foo(); + int foo() const; + + ~A() { foo(); } + }; + + template class A; +}