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. llvm-svn: 100562
This commit is contained in:
parent
4dac890600
commit
a8ae222d0e
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
||||
|
|
|
@ -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
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
static CXXRecordDecl *FindDeclaringClass(NamedDecl *D) {
|
||||
DeclContext *DC = D->getDeclContext();
|
||||
AccessTarget(ASTContext &Context,
|
||||
MemberNonce _,
|
||||
CXXRecordDecl *NamingClass,
|
||||
DeclAccessPair FoundDecl,
|
||||
QualType BaseObjectType)
|
||||
: AccessedEntity(Context, Member, NamingClass, FoundDecl, BaseObjectType) {
|
||||
initialize();
|
||||
}
|
||||
|
||||
// This can only happen at top: enum decls only "publish" their
|
||||
// immediate members.
|
||||
if (isa<EnumDecl>(DC))
|
||||
DC = cast<EnumDecl>(DC)->getDeclContext();
|
||||
AccessTarget(ASTContext &Context,
|
||||
BaseNonce _,
|
||||
CXXRecordDecl *BaseClass,
|
||||
CXXRecordDecl *DerivedClass,
|
||||
AccessSpecifier Access)
|
||||
: AccessedEntity(Context, Base, BaseClass, DerivedClass, Access) {
|
||||
initialize();
|
||||
}
|
||||
|
||||
CXXRecordDecl *DeclaringClass = cast<CXXRecordDecl>(DC);
|
||||
while (DeclaringClass->isAnonymousStructOrUnion())
|
||||
DeclaringClass = cast<CXXRecordDecl>(DeclaringClass->getDeclContext());
|
||||
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;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
/// 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;
|
||||
|
||||
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,
|
||||
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,14 +363,14 @@ 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,
|
||||
static AccessResult MatchesFriend(Sema &S,
|
||||
const EffectiveContext &EC,
|
||||
CanQualType Friend) {
|
||||
if (const RecordType *RT = Friend->getAs<RecordType>())
|
||||
|
@ -234,17 +378,17 @@ static Sema::AccessResult MatchesFriend(Sema &S,
|
|||
|
||||
// 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,
|
||||
static AccessResult MatchesFriend(Sema &S,
|
||||
const EffectiveContext &EC,
|
||||
ClassTemplateDecl *Friend) {
|
||||
Sema::AccessResult OnFailure = Sema::AR_inaccessible;
|
||||
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,
|
||||
static AccessResult MatchesFriend(Sema &S,
|
||||
const EffectiveContext &EC,
|
||||
FunctionDecl *Friend) {
|
||||
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) {
|
||||
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,
|
||||
static AccessResult MatchesFriend(Sema &S,
|
||||
const EffectiveContext &EC,
|
||||
FunctionTemplateDecl *Friend) {
|
||||
if (EC.Functions.empty()) return Sema::AR_inaccessible;
|
||||
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,7 +487,7 @@ static Sema::AccessResult MatchesFriend(Sema &S,
|
|||
|
||||
/// Determines whether the given friend declaration matches anything
|
||||
/// in the effective context.
|
||||
static Sema::AccessResult MatchesFriend(Sema &S,
|
||||
static AccessResult MatchesFriend(Sema &S,
|
||||
const EffectiveContext &EC,
|
||||
FriendDecl *FriendD) {
|
||||
if (TypeSourceInfo *T = FriendD->getFriendType())
|
||||
|
@ -367,10 +511,10 @@ static Sema::AccessResult MatchesFriend(Sema &S,
|
|||
return MatchesFriend(S, EC, cast<FunctionDecl>(Friend));
|
||||
}
|
||||
|
||||
static Sema::AccessResult GetFriendKind(Sema &S,
|
||||
static AccessResult GetFriendKind(Sema &S,
|
||||
const EffectiveContext &EC,
|
||||
const CXXRecordDecl *Class) {
|
||||
Sema::AccessResult OnFailure = Sema::AR_inaccessible;
|
||||
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:
|
||||
case AR_inaccessible:
|
||||
continue;
|
||||
|
||||
case AR_dependent:
|
||||
OnFailure = AR_dependent;
|
||||
break;
|
||||
|
||||
case Sema::AR_dependent:
|
||||
OnFailure = Sema::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,
|
||||
static AccessResult HasAccess(Sema &S,
|
||||
const EffectiveContext &EC,
|
||||
const CXXRecordDecl *NamingClass,
|
||||
AccessSpecifier Access) {
|
||||
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 (Access == AS_private) {
|
||||
if (ECRecord == NamingClass)
|
||||
return Sema::AR_accessible;
|
||||
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;
|
||||
}
|
||||
|
||||
return GetFriendKind(S, EC, NamingClass);
|
||||
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,
|
||||
|
@ -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),
|
||||
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,
|
||||
static AccessResult IsAccessible(Sema &S,
|
||||
const EffectiveContext &EC,
|
||||
const Sema::AccessedEntity &Entity) {
|
||||
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,
|
||||
static void DelayDependentAccess(Sema &S,
|
||||
const EffectiveContext &EC,
|
||||
SourceLocation Loc,
|
||||
const Sema::AccessedEntity &Entity) {
|
||||
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,
|
||||
static AccessResult CheckEffectiveAccess(Sema &S,
|
||||
const EffectiveContext &EC,
|
||||
SourceLocation Loc,
|
||||
const Sema::AccessedEntity &Entity) {
|
||||
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;
|
||||
return AR_inaccessible;
|
||||
|
||||
case Sema::AR_accessible:
|
||||
break;
|
||||
case AR_accessible:
|
||||
return AR_accessible;
|
||||
}
|
||||
|
||||
// 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<FieldDecl>(D) ||
|
||||
(isa<CXXMethodDecl>(D) && cast<CXXMethodDecl>(D)->isInstance())) {
|
||||
// FIXME: implement [class.protected]
|
||||
}
|
||||
}
|
||||
|
||||
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,16 +1037,25 @@ void Sema::HandleDependentAccessCheck(const DependentDiagnostic &DD,
|
|||
if (!TargetD) return;
|
||||
|
||||
if (DD.isAccessToMember()) {
|
||||
AccessedEntity Entity(Context,
|
||||
AccessedEntity::Member,
|
||||
cast<CXXRecordDecl>(NamingD),
|
||||
Access,
|
||||
cast<NamedDecl>(TargetD));
|
||||
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 {
|
||||
AccessedEntity Entity(Context,
|
||||
AccessedEntity::Base,
|
||||
AccessTarget Entity(Context,
|
||||
AccessTarget::Base,
|
||||
cast<CXXRecordDecl>(TargetD),
|
||||
cast<CXXRecordDecl>(NamingD),
|
||||
Access);
|
||||
|
@ -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<PointerType>()->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<CXXRecordDecl>(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<CXXRecordDecl>(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<CXXRecordDecl>(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<CXXRecordDecl>(Base->getAs<RecordType>()->getDecl());
|
||||
DerivedD = cast<CXXRecordDecl>(Derived->getAs<RecordType>()->getDecl());
|
||||
|
||||
AccessedEntity Entity(Context, AccessedEntity::Base, BaseD, DerivedD,
|
||||
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);
|
||||
|
|
|
@ -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<FieldDecl>(D)) return true;
|
||||
|
||||
if (isa<CXXMethodDecl>(D))
|
||||
return !cast<CXXMethodDecl>(D)->isStatic();
|
||||
|
||||
if (isa<FunctionTemplateDecl>(D)) {
|
||||
D = cast<FunctionTemplateDecl>(D)->getTemplatedDecl();
|
||||
return !cast<CXXMethodDecl>(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<CXXRecordDecl*, 4> 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<CXXRecordDecl>(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<PointerType>()->getPointeeType();
|
||||
}
|
||||
R.setBaseObjectType(BaseType);
|
||||
|
||||
NestedNameSpecifier *Qualifier =
|
||||
static_cast<NestedNameSpecifier*>(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();
|
||||
|
|
|
@ -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<MemberExpr>(NakedMemExpr)) {
|
||||
MemExpr = cast<MemberExpr>(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<ParenExpr>(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));
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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<typename T> class A {
|
||||
protected:
|
||||
int foo();
|
||||
int foo() const;
|
||||
|
||||
~A() { foo(); }
|
||||
};
|
||||
|
||||
template class A<int>;
|
||||
}
|
Loading…
Reference in New Issue