diff options
-rw-r--r-- | include/clang/AST/Decl.h | 4 | ||||
-rw-r--r-- | include/clang/AST/DeclAccessPair.h | 72 | ||||
-rw-r--r-- | include/clang/AST/DeclCXX.h | 5 | ||||
-rw-r--r-- | include/clang/AST/DependentDiagnostic.h | 9 | ||||
-rw-r--r-- | include/clang/AST/Expr.h | 10 | ||||
-rw-r--r-- | include/clang/AST/UnresolvedSet.h | 51 | ||||
-rw-r--r-- | lib/AST/Decl.cpp | 18 | ||||
-rw-r--r-- | lib/AST/Expr.cpp | 6 | ||||
-rw-r--r-- | lib/Sema/Lookup.h | 13 | ||||
-rw-r--r-- | lib/Sema/Sema.h | 24 | ||||
-rw-r--r-- | lib/Sema/SemaAccess.cpp | 631 | ||||
-rw-r--r-- | lib/Sema/SemaExpr.cpp | 33 | ||||
-rw-r--r-- | lib/Sema/SemaOverload.cpp | 6 | ||||
-rw-r--r-- | lib/Sema/SemaType.cpp | 10 | ||||
-rw-r--r-- | test/CXX/class.access/class.protected/p1.cpp | 387 |
15 files changed, 969 insertions, 310 deletions
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<NamedDecl*>(uintptr_t(AS) | + reinterpret_cast<uintptr_t>(D)); + } + + operator NamedDecl*() const { return getDecl(); } + NamedDecl *operator->() const { return getDecl(); } +}; +} + +// Take a moment to tell SmallVector that DeclAccessPair is POD. +namespace llvm { +template<typename> struct isPodLike; +template<> struct isPodLike<clang::DeclAccessPair> { + 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 <iterator> #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<NamedDecl*>(uintptr_t(AS) | - reinterpret_cast<uintptr_t>(D)); - } - - operator NamedDecl*() const { return getDecl(); } - NamedDecl *operator->() const { return getDecl(); } -}; -} - -// Take a moment to tell SmallVector that this is POD. -namespace llvm { -template<typename> struct isPodLike; -template<> struct isPodLike<clang::DeclAccessPair> { - 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<UsingShadowDecl>(D)) + D = cast<UsingShadowDecl>(D)->getTargetDecl(); + + if (isa<FieldDecl>(D)) + return true; + if (isa<CXXMethodDecl>(D)) + return cast<CXXMethodDecl>(D)->isInstance(); + if (isa<FunctionTemplateDecl>(D)) + return cast<CXXMethodDecl>(cast<FunctionTemplateDecl>(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<CXXRecordDecl>(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<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) {} @@ -109,22 +130,145 @@ struct EffectiveContext { 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 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<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; +}; + } -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<EnumDecl>(DC)) - DC = cast<EnumDecl>(DC)->getDeclContext(); + if (Derived == Target) return AR_accessible; - CXXRecordDecl *DeclaringClass = cast<CXXRecordDecl>(DC); - while (DeclaringClass->isAnonymousStructOrUnion()) - DeclaringClass = cast<CXXRecordDecl>(DeclaringClass->getDeclContext()); - return DeclaringClass; + 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 { + // 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<RecordType>()) return MatchesFriend(S, EC, cast<CXXRecordDecl>(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<FunctionDecl*>::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<FunctionDecl*>::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<FunctionDecl>(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<CXXRecordDecl*>(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<CXXRecordDecl*>(Base), + P |