forked from OSchip/llvm-project
1966 lines
71 KiB
C++
1966 lines
71 KiB
C++
//===---- SemaAccess.cpp - C++ Access Control -------------------*- C++ -*-===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file provides Sema routines for C++ access control semantics.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "clang/Basic/Specifiers.h"
|
|
#include "clang/Sema/SemaInternal.h"
|
|
#include "clang/AST/ASTContext.h"
|
|
#include "clang/AST/CXXInheritance.h"
|
|
#include "clang/AST/DeclCXX.h"
|
|
#include "clang/AST/DeclFriend.h"
|
|
#include "clang/AST/DeclObjC.h"
|
|
#include "clang/AST/DependentDiagnostic.h"
|
|
#include "clang/AST/ExprCXX.h"
|
|
#include "clang/Sema/DelayedDiagnostic.h"
|
|
#include "clang/Sema/Initialization.h"
|
|
#include "clang/Sema/Lookup.h"
|
|
|
|
using namespace clang;
|
|
using namespace sema;
|
|
|
|
/// A copy of Sema's enum without AR_delayed.
|
|
enum AccessResult {
|
|
AR_accessible,
|
|
AR_inaccessible,
|
|
AR_dependent
|
|
};
|
|
|
|
/// SetMemberAccessSpecifier - Set the access specifier of a member.
|
|
/// Returns true on error (when the previous member decl access specifier
|
|
/// is different from the new member decl access specifier).
|
|
bool Sema::SetMemberAccessSpecifier(NamedDecl *MemberDecl,
|
|
NamedDecl *PrevMemberDecl,
|
|
AccessSpecifier LexicalAS) {
|
|
if (!PrevMemberDecl) {
|
|
// Use the lexical access specifier.
|
|
MemberDecl->setAccess(LexicalAS);
|
|
return false;
|
|
}
|
|
|
|
// C++ [class.access.spec]p3: When a member is redeclared its access
|
|
// specifier must be same as its initial declaration.
|
|
if (LexicalAS != AS_none && LexicalAS != PrevMemberDecl->getAccess()) {
|
|
Diag(MemberDecl->getLocation(),
|
|
diag::err_class_redeclared_with_different_access)
|
|
<< MemberDecl << LexicalAS;
|
|
Diag(PrevMemberDecl->getLocation(), diag::note_previous_access_declaration)
|
|
<< PrevMemberDecl << PrevMemberDecl->getAccess();
|
|
|
|
MemberDecl->setAccess(LexicalAS);
|
|
return true;
|
|
}
|
|
|
|
MemberDecl->setAccess(PrevMemberDecl->getAccess());
|
|
return false;
|
|
}
|
|
|
|
static CXXRecordDecl *FindDeclaringClass(NamedDecl *D) {
|
|
DeclContext *DC = D->getDeclContext();
|
|
|
|
// This can only happen at top: enum decls only "publish" their
|
|
// immediate members.
|
|
if (isa<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(nullptr), Dependent(false) {}
|
|
|
|
explicit EffectiveContext(DeclContext *DC)
|
|
: Inner(DC),
|
|
Dependent(DC->isDependentContext()) {
|
|
|
|
// An implicit deduction guide is semantically in the context enclosing the
|
|
// class template, but for access purposes behaves like the constructor
|
|
// from which it was produced.
|
|
if (auto *DGD = dyn_cast<CXXDeductionGuideDecl>(DC)) {
|
|
if (DGD->isImplicit()) {
|
|
DC = DGD->getCorrespondingConstructor();
|
|
if (!DC) {
|
|
// The copy deduction candidate doesn't have a corresponding
|
|
// constructor.
|
|
DC = cast<DeclContext>(DGD->getDeducedTemplate()->getTemplatedDecl());
|
|
}
|
|
}
|
|
}
|
|
|
|
// C++11 [class.access.nest]p1:
|
|
// A nested class is a member and as such has the same access
|
|
// rights as any other member.
|
|
// C++11 [class.access]p2:
|
|
// A member of a class can also access all the names to which
|
|
// the class has access. A local class of a member function
|
|
// may access the same names that the member function itself
|
|
// may access.
|
|
// This almost implies that the privileges of nesting are transitive.
|
|
// Technically it says nothing about the local classes of non-member
|
|
// functions (which can gain privileges through friendship), but we
|
|
// take that as an oversight.
|
|
while (true) {
|
|
// We want to add canonical declarations to the EC lists for
|
|
// simplicity of checking, but we need to walk up through the
|
|
// actual current DC chain. Otherwise, something like a local
|
|
// extern or friend which happens to be the canonical
|
|
// declaration will really mess us up.
|
|
|
|
if (isa<CXXRecordDecl>(DC)) {
|
|
CXXRecordDecl *Record = cast<CXXRecordDecl>(DC);
|
|
Records.push_back(Record->getCanonicalDecl());
|
|
DC = Record->getDeclContext();
|
|
} else if (isa<FunctionDecl>(DC)) {
|
|
FunctionDecl *Function = cast<FunctionDecl>(DC);
|
|
Functions.push_back(Function->getCanonicalDecl());
|
|
if (Function->getFriendObjectKind())
|
|
DC = Function->getLexicalDeclContext();
|
|
else
|
|
DC = Function->getDeclContext();
|
|
} else if (DC->isFileContext()) {
|
|
break;
|
|
} else {
|
|
DC = DC->getParent();
|
|
}
|
|
}
|
|
}
|
|
|
|
bool isDependent() const { return Dependent; }
|
|
|
|
bool includesClass(const CXXRecordDecl *R) const {
|
|
R = R->getCanonicalDecl();
|
|
return llvm::find(Records, R) != Records.end();
|
|
}
|
|
|
|
/// Retrieves the innermost "useful" context. Can be null if we're
|
|
/// doing access-control without privileges.
|
|
DeclContext *getInnerContext() const {
|
|
return Inner;
|
|
}
|
|
|
|
typedef SmallVectorImpl<CXXRecordDecl*>::const_iterator record_iterator;
|
|
|
|
DeclContext *Inner;
|
|
SmallVector<FunctionDecl*, 4> Functions;
|
|
SmallVector<CXXRecordDecl*, 4> Records;
|
|
bool Dependent;
|
|
};
|
|
|
|
/// Like sema::AccessedEntity, but kindly lets us scribble all over
|
|
/// it.
|
|
struct AccessTarget : public AccessedEntity {
|
|
AccessTarget(const AccessedEntity &Entity)
|
|
: AccessedEntity(Entity) {
|
|
initialize();
|
|
}
|
|
|
|
AccessTarget(ASTContext &Context,
|
|
MemberNonce _,
|
|
CXXRecordDecl *NamingClass,
|
|
DeclAccessPair FoundDecl,
|
|
QualType BaseObjectType)
|
|
: AccessedEntity(Context.getDiagAllocator(), Member, NamingClass,
|
|
FoundDecl, BaseObjectType) {
|
|
initialize();
|
|
}
|
|
|
|
AccessTarget(ASTContext &Context,
|
|
BaseNonce _,
|
|
CXXRecordDecl *BaseClass,
|
|
CXXRecordDecl *DerivedClass,
|
|
AccessSpecifier Access)
|
|
: AccessedEntity(Context.getDiagAllocator(), Base, BaseClass, DerivedClass,
|
|
Access) {
|
|
initialize();
|
|
}
|
|
|
|
bool isInstanceMember() const {
|
|
return (isMemberAccess() && getTargetDecl()->isCXXInstanceMember());
|
|
}
|
|
|
|
bool hasInstanceContext() const {
|
|
return HasInstanceContext;
|
|
}
|
|
|
|
class SavedInstanceContext {
|
|
public:
|
|
SavedInstanceContext(SavedInstanceContext &&S)
|
|
: Target(S.Target), Has(S.Has) {
|
|
S.Target = nullptr;
|
|
}
|
|
~SavedInstanceContext() {
|
|
if (Target)
|
|
Target->HasInstanceContext = Has;
|
|
}
|
|
|
|
private:
|
|
friend struct AccessTarget;
|
|
explicit SavedInstanceContext(AccessTarget &Target)
|
|
: Target(&Target), Has(Target.HasInstanceContext) {}
|
|
AccessTarget *Target;
|
|
bool Has;
|
|
};
|
|
|
|
SavedInstanceContext saveInstanceContext() {
|
|
return SavedInstanceContext(*this);
|
|
}
|
|
|
|
void suppressInstanceContext() {
|
|
HasInstanceContext = false;
|
|
}
|
|
|
|
const CXXRecordDecl *resolveInstanceContext(Sema &S) const {
|
|
assert(HasInstanceContext);
|
|
if (CalculatedInstanceContext)
|
|
return InstanceContext;
|
|
|
|
CalculatedInstanceContext = true;
|
|
DeclContext *IC = S.computeDeclContext(getBaseObjectType());
|
|
InstanceContext = (IC ? cast<CXXRecordDecl>(IC)->getCanonicalDecl()
|
|
: nullptr);
|
|
return InstanceContext;
|
|
}
|
|
|
|
const CXXRecordDecl *getDeclaringClass() const {
|
|
return DeclaringClass;
|
|
}
|
|
|
|
/// The "effective" naming class is the canonical non-anonymous
|
|
/// class containing the actual naming class.
|
|
const CXXRecordDecl *getEffectiveNamingClass() const {
|
|
const CXXRecordDecl *namingClass = getNamingClass();
|
|
while (namingClass->isAnonymousStructOrUnion())
|
|
namingClass = cast<CXXRecordDecl>(namingClass->getParent());
|
|
return namingClass->getCanonicalDecl();
|
|
}
|
|
|
|
private:
|
|
void initialize() {
|
|
HasInstanceContext = (isMemberAccess() &&
|
|
!getBaseObjectType().isNull() &&
|
|
getTargetDecl()->isCXXInstanceMember());
|
|
CalculatedInstanceContext = false;
|
|
InstanceContext = nullptr;
|
|
|
|
if (isMemberAccess())
|
|
DeclaringClass = FindDeclaringClass(getTargetDecl());
|
|
else
|
|
DeclaringClass = getBaseClass();
|
|
DeclaringClass = DeclaringClass->getCanonicalDecl();
|
|
}
|
|
|
|
bool HasInstanceContext : 1;
|
|
mutable bool CalculatedInstanceContext : 1;
|
|
mutable const CXXRecordDecl *InstanceContext;
|
|
const CXXRecordDecl *DeclaringClass;
|
|
};
|
|
|
|
}
|
|
|
|
/// Checks whether one class might instantiate to the other.
|
|
static bool MightInstantiateTo(const CXXRecordDecl *From,
|
|
const CXXRecordDecl *To) {
|
|
// Declaration names are always preserved by instantiation.
|
|
if (From->getDeclName() != To->getDeclName())
|
|
return false;
|
|
|
|
const DeclContext *FromDC = From->getDeclContext()->getPrimaryContext();
|
|
const DeclContext *ToDC = To->getDeclContext()->getPrimaryContext();
|
|
if (FromDC == ToDC) return true;
|
|
if (FromDC->isFileContext() || ToDC->isFileContext()) return false;
|
|
|
|
// Be conservative.
|
|
return true;
|
|
}
|
|
|
|
/// Checks whether one class is derived from another, inclusively.
|
|
/// Properly indicates when it couldn't be determined due to
|
|
/// dependence.
|
|
///
|
|
/// This should probably be donated to AST or at least Sema.
|
|
static AccessResult IsDerivedFromInclusive(const CXXRecordDecl *Derived,
|
|
const CXXRecordDecl *Target) {
|
|
assert(Derived->getCanonicalDecl() == Derived);
|
|
assert(Target->getCanonicalDecl() == Target);
|
|
|
|
if (Derived == Target) return AR_accessible;
|
|
|
|
bool CheckDependent = Derived->isDependentContext();
|
|
if (CheckDependent && MightInstantiateTo(Derived, Target))
|
|
return AR_dependent;
|
|
|
|
AccessResult OnFailure = AR_inaccessible;
|
|
SmallVector<const CXXRecordDecl*, 8> Queue; // actually a stack
|
|
|
|
while (true) {
|
|
if (Derived->isDependentContext() && !Derived->hasDefinition() &&
|
|
!Derived->isLambda())
|
|
return AR_dependent;
|
|
|
|
for (const auto &I : Derived->bases()) {
|
|
const CXXRecordDecl *RD;
|
|
|
|
QualType T = I.getType();
|
|
if (const RecordType *RT = T->getAs<RecordType>()) {
|
|
RD = cast<CXXRecordDecl>(RT->getDecl());
|
|
} else if (const InjectedClassNameType *IT
|
|
= T->getAs<InjectedClassNameType>()) {
|
|
RD = IT->getDecl();
|
|
} else {
|
|
assert(T->isDependentType() && "non-dependent base wasn't a record?");
|
|
OnFailure = AR_dependent;
|
|
continue;
|
|
}
|
|
|
|
RD = RD->getCanonicalDecl();
|
|
if (RD == Target) return AR_accessible;
|
|
if (CheckDependent && MightInstantiateTo(RD, Target))
|
|
OnFailure = AR_dependent;
|
|
|
|
Queue.push_back(RD);
|
|
}
|
|
|
|
if (Queue.empty()) break;
|
|
|
|
Derived = Queue.pop_back_val();
|
|
}
|
|
|
|
return OnFailure;
|
|
}
|
|
|
|
|
|
static bool MightInstantiateTo(Sema &S, DeclContext *Context,
|
|
DeclContext *Friend) {
|
|
if (Friend == Context)
|
|
return true;
|
|
|
|
assert(!Friend->isDependentContext() &&
|
|
"can't handle friends with dependent contexts here");
|
|
|
|
if (!Context->isDependentContext())
|
|
return false;
|
|
|
|
if (Friend->isFileContext())
|
|
return false;
|
|
|
|
// TODO: this is very conservative
|
|
return true;
|
|
}
|
|
|
|
// Asks whether the type in 'context' can ever instantiate to the type
|
|
// in 'friend'.
|
|
static bool MightInstantiateTo(Sema &S, CanQualType Context, CanQualType Friend) {
|
|
if (Friend == Context)
|
|
return true;
|
|
|
|
if (!Friend->isDependentType() && !Context->isDependentType())
|
|
return false;
|
|
|
|
// TODO: this is very conservative.
|
|
return true;
|
|
}
|
|
|
|
static bool MightInstantiateTo(Sema &S,
|
|
FunctionDecl *Context,
|
|
FunctionDecl *Friend) {
|
|
if (Context->getDeclName() != Friend->getDeclName())
|
|
return false;
|
|
|
|
if (!MightInstantiateTo(S,
|
|
Context->getDeclContext(),
|
|
Friend->getDeclContext()))
|
|
return false;
|
|
|
|
CanQual<FunctionProtoType> FriendTy
|
|
= S.Context.getCanonicalType(Friend->getType())
|
|
->getAs<FunctionProtoType>();
|
|
CanQual<FunctionProtoType> ContextTy
|
|
= S.Context.getCanonicalType(Context->getType())
|
|
->getAs<FunctionProtoType>();
|
|
|
|
// There isn't any way that I know of to add qualifiers
|
|
// during instantiation.
|
|
if (FriendTy.getQualifiers() != ContextTy.getQualifiers())
|
|
return false;
|
|
|
|
if (FriendTy->getNumParams() != ContextTy->getNumParams())
|
|
return false;
|
|
|
|
if (!MightInstantiateTo(S, ContextTy->getReturnType(),
|
|
FriendTy->getReturnType()))
|
|
return false;
|
|
|
|
for (unsigned I = 0, E = FriendTy->getNumParams(); I != E; ++I)
|
|
if (!MightInstantiateTo(S, ContextTy->getParamType(I),
|
|
FriendTy->getParamType(I)))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool MightInstantiateTo(Sema &S,
|
|
FunctionTemplateDecl *Context,
|
|
FunctionTemplateDecl *Friend) {
|
|
return MightInstantiateTo(S,
|
|
Context->getTemplatedDecl(),
|
|
Friend->getTemplatedDecl());
|
|
}
|
|
|
|
static AccessResult MatchesFriend(Sema &S,
|
|
const EffectiveContext &EC,
|
|
const CXXRecordDecl *Friend) {
|
|
if (EC.includesClass(Friend))
|
|
return AR_accessible;
|
|
|
|
if (EC.isDependent()) {
|
|
for (const CXXRecordDecl *Context : EC.Records) {
|
|
if (MightInstantiateTo(Context, Friend))
|
|
return AR_dependent;
|
|
}
|
|
}
|
|
|
|
return AR_inaccessible;
|
|
}
|
|
|
|
static AccessResult MatchesFriend(Sema &S,
|
|
const EffectiveContext &EC,
|
|
CanQualType Friend) {
|
|
if (const RecordType *RT = Friend->getAs<RecordType>())
|
|
return MatchesFriend(S, EC, cast<CXXRecordDecl>(RT->getDecl()));
|
|
|
|
// TODO: we can do better than this
|
|
if (Friend->isDependentType())
|
|
return AR_dependent;
|
|
|
|
return AR_inaccessible;
|
|
}
|
|
|
|
/// Determines whether the given friend class template matches
|
|
/// anything in the effective context.
|
|
static AccessResult MatchesFriend(Sema &S,
|
|
const EffectiveContext &EC,
|
|
ClassTemplateDecl *Friend) {
|
|
AccessResult OnFailure = AR_inaccessible;
|
|
|
|
// Check whether the friend is the template of a class in the
|
|
// context chain.
|
|
for (SmallVectorImpl<CXXRecordDecl*>::const_iterator
|
|
I = EC.Records.begin(), E = EC.Records.end(); I != E; ++I) {
|
|
CXXRecordDecl *Record = *I;
|
|
|
|
// Figure out whether the current class has a template:
|
|
ClassTemplateDecl *CTD;
|
|
|
|
// A specialization of the template...
|
|
if (isa<ClassTemplateSpecializationDecl>(Record)) {
|
|
CTD = cast<ClassTemplateSpecializationDecl>(Record)
|
|
->getSpecializedTemplate();
|
|
|
|
// ... or the template pattern itself.
|
|
} else {
|
|
CTD = Record->getDescribedClassTemplate();
|
|
if (!CTD) continue;
|
|
}
|
|
|
|
// It's a match.
|
|
if (Friend == CTD->getCanonicalDecl())
|
|
return AR_accessible;
|
|
|
|
// If the context isn't dependent, it can't be a dependent match.
|
|
if (!EC.isDependent())
|
|
continue;
|
|
|
|
// If the template names don't match, it can't be a dependent
|
|
// match.
|
|
if (CTD->getDeclName() != Friend->getDeclName())
|
|
continue;
|
|
|
|
// If the class's context can't instantiate to the friend's
|
|
// context, it can't be a dependent match.
|
|
if (!MightInstantiateTo(S, CTD->getDeclContext(),
|
|
Friend->getDeclContext()))
|
|
continue;
|
|
|
|
// Otherwise, it's a dependent match.
|
|
OnFailure = AR_dependent;
|
|
}
|
|
|
|
return OnFailure;
|
|
}
|
|
|
|
/// Determines whether the given friend function matches anything in
|
|
/// the effective context.
|
|
static AccessResult MatchesFriend(Sema &S,
|
|
const EffectiveContext &EC,
|
|
FunctionDecl *Friend) {
|
|
AccessResult OnFailure = AR_inaccessible;
|
|
|
|
for (SmallVectorImpl<FunctionDecl*>::const_iterator
|
|
I = EC.Functions.begin(), E = EC.Functions.end(); I != E; ++I) {
|
|
if (Friend == *I)
|
|
return AR_accessible;
|
|
|
|
if (EC.isDependent() && MightInstantiateTo(S, *I, Friend))
|
|
OnFailure = AR_dependent;
|
|
}
|
|
|
|
return OnFailure;
|
|
}
|
|
|
|
/// Determines whether the given friend function template matches
|
|
/// anything in the effective context.
|
|
static AccessResult MatchesFriend(Sema &S,
|
|
const EffectiveContext &EC,
|
|
FunctionTemplateDecl *Friend) {
|
|
if (EC.Functions.empty()) return AR_inaccessible;
|
|
|
|
AccessResult OnFailure = AR_inaccessible;
|
|
|
|
for (SmallVectorImpl<FunctionDecl*>::const_iterator
|
|
I = EC.Functions.begin(), E = EC.Functions.end(); I != E; ++I) {
|
|
|
|
FunctionTemplateDecl *FTD = (*I)->getPrimaryTemplate();
|
|
if (!FTD)
|
|
FTD = (*I)->getDescribedFunctionTemplate();
|
|
if (!FTD)
|
|
continue;
|
|
|
|
FTD = FTD->getCanonicalDecl();
|
|
|
|
if (Friend == FTD)
|
|
return AR_accessible;
|
|
|
|
if (EC.isDependent() && MightInstantiateTo(S, FTD, Friend))
|
|
OnFailure = AR_dependent;
|
|
}
|
|
|
|
return OnFailure;
|
|
}
|
|
|
|
/// Determines whether the given friend declaration matches anything
|
|
/// in the effective context.
|
|
static AccessResult MatchesFriend(Sema &S,
|
|
const EffectiveContext &EC,
|
|
FriendDecl *FriendD) {
|
|
// Whitelist accesses if there's an invalid or unsupported friend
|
|
// declaration.
|
|
if (FriendD->isInvalidDecl() || FriendD->isUnsupportedFriend())
|
|
return AR_accessible;
|
|
|
|
if (TypeSourceInfo *T = FriendD->getFriendType())
|
|
return MatchesFriend(S, EC, T->getType()->getCanonicalTypeUnqualified());
|
|
|
|
NamedDecl *Friend
|
|
= cast<NamedDecl>(FriendD->getFriendDecl()->getCanonicalDecl());
|
|
|
|
// FIXME: declarations with dependent or templated scope.
|
|
|
|
if (isa<ClassTemplateDecl>(Friend))
|
|
return MatchesFriend(S, EC, cast<ClassTemplateDecl>(Friend));
|
|
|
|
if (isa<FunctionTemplateDecl>(Friend))
|
|
return MatchesFriend(S, EC, cast<FunctionTemplateDecl>(Friend));
|
|
|
|
if (isa<CXXRecordDecl>(Friend))
|
|
return MatchesFriend(S, EC, cast<CXXRecordDecl>(Friend));
|
|
|
|
assert(isa<FunctionDecl>(Friend) && "unknown friend decl kind");
|
|
return MatchesFriend(S, EC, cast<FunctionDecl>(Friend));
|
|
}
|
|
|
|
static AccessResult GetFriendKind(Sema &S,
|
|
const EffectiveContext &EC,
|
|
const CXXRecordDecl *Class) {
|
|
AccessResult OnFailure = AR_inaccessible;
|
|
|
|
// Okay, check friends.
|
|
for (auto *Friend : Class->friends()) {
|
|
switch (MatchesFriend(S, EC, Friend)) {
|
|
case AR_accessible:
|
|
return AR_accessible;
|
|
|
|
case AR_inaccessible:
|
|
continue;
|
|
|
|
case AR_dependent:
|
|
OnFailure = AR_dependent;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// That's it, give up.
|
|
return OnFailure;
|
|
}
|
|
|
|
namespace {
|
|
|
|
/// A helper class for checking for a friend which will grant access
|
|
/// to a protected instance member.
|
|
struct ProtectedFriendContext {
|
|
Sema &S;
|
|
const EffectiveContext &EC;
|
|
const CXXRecordDecl *NamingClass;
|
|
bool CheckDependent;
|
|
bool EverDependent;
|
|
|
|
/// The path down to the current base class.
|
|
SmallVector<const CXXRecordDecl*, 20> CurPath;
|
|
|
|
ProtectedFriendContext(Sema &S, const EffectiveContext &EC,
|
|
const CXXRecordDecl *InstanceContext,
|
|
const CXXRecordDecl *NamingClass)
|
|
: S(S), EC(EC), NamingClass(NamingClass),
|
|
CheckDependent(InstanceContext->isDependentContext() ||
|
|
NamingClass->isDependentContext()),
|
|
EverDependent(false) {}
|
|
|
|
/// Check classes in the current path for friendship, starting at
|
|
/// the given index.
|
|
bool checkFriendshipAlongPath(unsigned I) {
|
|
assert(I < CurPath.size());
|
|
for (unsigned E = CurPath.size(); I != E; ++I) {
|
|
switch (GetFriendKind(S, EC, CurPath[I])) {
|
|
case AR_accessible: return true;
|
|
case AR_inaccessible: continue;
|
|
case AR_dependent: EverDependent = true; continue;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// Perform a search starting at the given class.
|
|
///
|
|
/// PrivateDepth is the index of the last (least derived) class
|
|
/// along the current path such that a notional public member of
|
|
/// the final class in the path would have access in that class.
|
|
bool findFriendship(const CXXRecordDecl *Cur, unsigned PrivateDepth) {
|
|
// If we ever reach the naming class, check the current path for
|
|
// friendship. We can also stop recursing because we obviously
|
|
// won't find the naming class there again.
|
|
if (Cur == NamingClass)
|
|
return checkFriendshipAlongPath(PrivateDepth);
|
|
|
|
if (CheckDependent && MightInstantiateTo(Cur, NamingClass))
|
|
EverDependent = true;
|
|
|
|
// Recurse into the base classes.
|
|
for (const auto &I : Cur->bases()) {
|
|
// If this is private inheritance, then a public member of the
|
|
// base will not have any access in classes derived from Cur.
|
|
unsigned BasePrivateDepth = PrivateDepth;
|
|
if (I.getAccessSpecifier() == AS_private)
|
|
BasePrivateDepth = CurPath.size() - 1;
|
|
|
|
const CXXRecordDecl *RD;
|
|
|
|
QualType T = I.getType();
|
|
if (const RecordType *RT = T->getAs<RecordType>()) {
|
|
RD = cast<CXXRecordDecl>(RT->getDecl());
|
|
} else if (const InjectedClassNameType *IT
|
|
= T->getAs<InjectedClassNameType>()) {
|
|
RD = IT->getDecl();
|
|
} else {
|
|
assert(T->isDependentType() && "non-dependent base wasn't a record?");
|
|
EverDependent = true;
|
|
continue;
|
|
}
|
|
|
|
// Recurse. We don't need to clean up if this returns true.
|
|
CurPath.push_back(RD);
|
|
if (findFriendship(RD->getCanonicalDecl(), BasePrivateDepth))
|
|
return true;
|
|
CurPath.pop_back();
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool findFriendship(const CXXRecordDecl *Cur) {
|
|
assert(CurPath.empty());
|
|
CurPath.push_back(Cur);
|
|
return findFriendship(Cur, 0);
|
|
}
|
|
};
|
|
}
|
|
|
|
/// Search for a class P that EC is a friend of, under the constraint
|
|
/// InstanceContext <= P
|
|
/// if InstanceContext exists, or else
|
|
/// NamingClass <= P
|
|
/// and with the additional restriction that a protected member of
|
|
/// NamingClass would have some natural access in P, which implicitly
|
|
/// imposes the constraint that P <= NamingClass.
|
|
///
|
|
/// This isn't quite the condition laid out in the standard.
|
|
/// Instead of saying that a notional protected member of NamingClass
|
|
/// would have to have some natural access in P, it says the actual
|
|
/// target has to have some natural access in P, which opens up the
|
|
/// possibility that the target (which is not necessarily a member
|
|
/// of NamingClass) might be more accessible along some path not
|
|
/// passing through it. That's really a bad idea, though, because it
|
|
/// introduces two problems:
|
|
/// - Most importantly, it breaks encapsulation because you can
|
|
/// access a forbidden base class's members by directly subclassing
|
|
/// it elsewhere.
|
|
/// - It also makes access substantially harder to compute because it
|
|
/// breaks the hill-climbing algorithm: knowing that the target is
|
|
/// accessible in some base class would no longer let you change
|
|
/// the question solely to whether the base class is accessible,
|
|
/// because the original target might have been more accessible
|
|
/// because of crazy subclassing.
|
|
/// So we don't implement that.
|
|
static AccessResult GetProtectedFriendKind(Sema &S, const EffectiveContext &EC,
|
|
const CXXRecordDecl *InstanceContext,
|
|
const CXXRecordDecl *NamingClass) {
|
|
assert(InstanceContext == nullptr ||
|
|
InstanceContext->getCanonicalDecl() == InstanceContext);
|
|
assert(NamingClass->getCanonicalDecl() == NamingClass);
|
|
|
|
// If we don't have an instance context, our constraints give us
|
|
// that NamingClass <= P <= NamingClass, i.e. P == NamingClass.
|
|
// This is just the usual friendship check.
|
|
if (!InstanceContext) return GetFriendKind(S, EC, NamingClass);
|
|
|
|
ProtectedFriendContext PRC(S, EC, InstanceContext, NamingClass);
|
|
if (PRC.findFriendship(InstanceContext)) return AR_accessible;
|
|
if (PRC.EverDependent) return AR_dependent;
|
|
return AR_inaccessible;
|
|
}
|
|
|
|
static AccessResult HasAccess(Sema &S,
|
|
const EffectiveContext &EC,
|
|
const CXXRecordDecl *NamingClass,
|
|
AccessSpecifier Access,
|
|
const AccessTarget &Target) {
|
|
assert(NamingClass->getCanonicalDecl() == NamingClass &&
|
|
"declaration should be canonicalized before being passed here");
|
|
|
|
if (Access == AS_public) return AR_accessible;
|
|
assert(Access == AS_private || Access == AS_protected);
|
|
|
|
AccessResult OnFailure = AR_inaccessible;
|
|
|
|
for (EffectiveContext::record_iterator
|
|
I = EC.Records.begin(), E = EC.Records.end(); I != E; ++I) {
|
|
// All the declarations in EC have been canonicalized, so pointer
|
|
// equality from this point on will work fine.
|
|
const CXXRecordDecl *ECRecord = *I;
|
|
|
|
// [B2] and [M2]
|
|
if (Access == AS_private) {
|
|
if (ECRecord == NamingClass)
|
|
return AR_accessible;
|
|
|
|
if (EC.isDependent() && MightInstantiateTo(ECRecord, NamingClass))
|
|
OnFailure = AR_dependent;
|
|
|
|
// [B3] and [M3]
|
|
} else {
|
|
assert(Access == AS_protected);
|
|
switch (IsDerivedFromInclusive(ECRecord, NamingClass)) {
|
|
case AR_accessible: break;
|
|
case AR_inaccessible: continue;
|
|
case AR_dependent: OnFailure = AR_dependent; continue;
|
|
}
|
|
|
|
// C++ [class.protected]p1:
|
|
// An additional access check beyond those described earlier in
|
|
// [class.access] is applied when a non-static data member or
|
|
// non-static member function is a protected member of its naming
|
|
// class. As described earlier, access to a protected member is
|
|
// granted because the reference occurs in a friend or member of
|
|
// some class C. If the access is to form a pointer to member,
|
|
// the nested-name-specifier shall name C or a class derived from
|
|
// C. All other accesses involve a (possibly implicit) object
|
|
// expression. In this case, the class of the object expression
|
|
// shall be C or a class derived from C.
|
|
//
|
|
// We interpret this as a restriction on [M3].
|
|
|
|
// In this part of the code, 'C' is just our context class ECRecord.
|
|
|
|
// These rules are different if we don't have an instance context.
|
|
if (!Target.hasInstanceContext()) {
|
|
// If it's not an instance member, these restrictions don't apply.
|
|
if (!Target.isInstanceMember()) return AR_accessible;
|
|
|
|
// If it's an instance member, use the pointer-to-member rule
|
|
// that the naming class has to be derived from the effective
|
|
// context.
|
|
|
|
// Emulate a MSVC bug where the creation of pointer-to-member
|
|
// to protected member of base class is allowed but only from
|
|
// static member functions.
|
|
if (S.getLangOpts().MSVCCompat && !EC.Functions.empty())
|
|
if (CXXMethodDecl* MD = dyn_cast<CXXMethodDecl>(EC.Functions.front()))
|
|
if (MD->isStatic()) return AR_accessible;
|
|
|
|
// Despite the standard's confident wording, there is a case
|
|
// where you can have an instance member that's neither in a
|
|
// pointer-to-member expression nor in a member access: when
|
|
// it names a field in an unevaluated context that can't be an
|
|
// implicit member. Pending clarification, we just apply the
|
|
// same naming-class restriction here.
|
|
// FIXME: we're probably not correctly adding the
|
|
// protected-member restriction when we retroactively convert
|
|
// an expression to being evaluated.
|
|
|
|
// We know that ECRecord derives from NamingClass. The
|
|
// restriction says to check whether NamingClass derives from
|
|
// ECRecord, but that's not really necessary: two distinct
|
|
// classes can't be recursively derived from each other. So
|
|
// along this path, we just need to check whether the classes
|
|
// are equal.
|
|
if (NamingClass == ECRecord) return AR_accessible;
|
|
|
|
// Otherwise, this context class tells us nothing; on to the next.
|
|
continue;
|
|
}
|
|
|
|
assert(Target.isInstanceMember());
|
|
|
|
const CXXRecordDecl *InstanceContext = Target.resolveInstanceContext(S);
|
|
if (!InstanceContext) {
|
|
OnFailure = AR_dependent;
|
|
continue;
|
|
}
|
|
|
|
switch (IsDerivedFromInclusive(InstanceContext, ECRecord)) {
|
|
case AR_accessible: return AR_accessible;
|
|
case AR_inaccessible: continue;
|
|
case AR_dependent: OnFailure = AR_dependent; continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
// [M3] and [B3] say that, if the target is protected in N, we grant
|
|
// access if the access occurs in a friend or member of some class P
|
|
// that's a subclass of N and where the target has some natural
|
|
// access in P. The 'member' aspect is easy to handle because P
|
|
// would necessarily be one of the effective-context records, and we
|
|
// address that above. The 'friend' aspect is completely ridiculous
|
|
// to implement because there are no restrictions at all on P
|
|
// *unless* the [class.protected] restriction applies. If it does,
|
|
// however, we should ignore whether the naming class is a friend,
|
|
// and instead rely on whether any potential P is a friend.
|
|
if (Access == AS_protected && Target.isInstanceMember()) {
|
|
// Compute the instance context if possible.
|
|
const CXXRecordDecl *InstanceContext = nullptr;
|
|
if (Target.hasInstanceContext()) {
|
|
InstanceContext = Target.resolveInstanceContext(S);
|
|
if (!InstanceContext) return AR_dependent;
|
|
}
|
|
|
|
switch (GetProtectedFriendKind(S, EC, InstanceContext, NamingClass)) {
|
|
case AR_accessible: return AR_accessible;
|
|
case AR_inaccessible: return OnFailure;
|
|
case AR_dependent: return AR_dependent;
|
|
}
|
|
llvm_unreachable("impossible friendship kind");
|
|
}
|
|
|
|
switch (GetFriendKind(S, EC, NamingClass)) {
|
|
case AR_accessible: return AR_accessible;
|
|
case AR_inaccessible: return OnFailure;
|
|
case AR_dependent: return AR_dependent;
|
|
}
|
|
|
|
// Silence bogus warnings
|
|
llvm_unreachable("impossible friendship kind");
|
|
}
|
|
|
|
/// Finds the best path from the naming class to the declaring class,
|
|
/// taking friend declarations into account.
|
|
///
|
|
/// C++0x [class.access.base]p5:
|
|
/// A member m is accessible at the point R when named in class N if
|
|
/// [M1] m as a member of N is public, or
|
|
/// [M2] m as a member of N is private, and R occurs in a member or
|
|
/// friend of class N, or
|
|
/// [M3] m as a member of N is protected, and R occurs in a member or
|
|
/// friend of class N, or in a member or friend of a class P
|
|
/// derived from N, where m as a member of P is public, private,
|
|
/// or protected, or
|
|
/// [M4] there exists a base class B of N that is accessible at R, and
|
|
/// m is accessible at R when named in class B.
|
|
///
|
|
/// C++0x [class.access.base]p4:
|
|
/// A base class B of N is accessible at R, if
|
|
/// [B1] an invented public member of B would be a public member of N, or
|
|
/// [B2] R occurs in a member or friend of class N, and an invented public
|
|
/// member of B would be a private or protected member of N, or
|
|
/// [B3] R occurs in a member or friend of a class P derived from N, and an
|
|
/// invented public member of B would be a private or protected member
|
|
/// of P, or
|
|
/// [B4] there exists a class S such that B is a base class of S accessible
|
|
/// at R and S is a base class of N accessible at R.
|
|
///
|
|
/// Along a single inheritance path we can restate both of these
|
|
/// iteratively:
|
|
///
|
|
/// First, we note that M1-4 are equivalent to B1-4 if the member is
|
|
/// treated as a notional base of its declaring class with inheritance
|
|
/// access equivalent to the member's access. Therefore we need only
|
|
/// ask whether a class B is accessible from a class N in context R.
|
|
///
|
|
/// Let B_1 .. B_n be the inheritance path in question (i.e. where
|
|
/// B_1 = N, B_n = B, and for all i, B_{i+1} is a direct base class of
|
|
/// B_i). For i in 1..n, we will calculate ACAB(i), the access to the
|
|
/// closest accessible base in the path:
|
|
/// Access(a, b) = (* access on the base specifier from a to b *)
|
|
/// Merge(a, forbidden) = forbidden
|
|
/// Merge(a, private) = forbidden
|
|
/// Merge(a, b) = min(a,b)
|
|
/// Accessible(c, forbidden) = false
|
|
/// Accessible(c, private) = (R is c) || IsFriend(c, R)
|
|
/// Accessible(c, protected) = (R derived from c) || IsFriend(c, R)
|
|
/// Accessible(c, public) = true
|
|
/// ACAB(n) = public
|
|
/// ACAB(i) =
|
|
/// let AccessToBase = Merge(Access(B_i, B_{i+1}), ACAB(i+1)) in
|
|
/// if Accessible(B_i, AccessToBase) then public else AccessToBase
|
|
///
|
|
/// B is an accessible base of N at R iff ACAB(1) = public.
|
|
///
|
|
/// \param FinalAccess the access of the "final step", or AS_public if
|
|
/// there is no final step.
|
|
/// \return null if friendship is dependent
|
|
static CXXBasePath *FindBestPath(Sema &S,
|
|
const EffectiveContext &EC,
|
|
AccessTarget &Target,
|
|
AccessSpecifier FinalAccess,
|
|
CXXBasePaths &Paths) {
|
|
// Derive the paths to the desired base.
|
|
const CXXRecordDecl *Derived = Target.getNamingClass();
|
|
const CXXRecordDecl *Base = Target.getDeclaringClass();
|
|
|
|
// FIXME: fail correctly when there are dependent paths.
|
|
bool isDerived = Derived->isDerivedFrom(const_cast<CXXRecordDecl*>(Base),
|
|
Paths);
|
|
assert(isDerived && "derived class not actually derived from base");
|
|
(void) isDerived;
|
|
|
|
CXXBasePath *BestPath = nullptr;
|
|
|
|
assert(FinalAccess != AS_none && "forbidden access after declaring class");
|
|
|
|
bool AnyDependent = false;
|
|
|
|
// Derive the friend-modified access along each path.
|
|
for (CXXBasePaths::paths_iterator PI = Paths.begin(), PE = Paths.end();
|
|
PI != PE; ++PI) {
|
|
AccessTarget::SavedInstanceContext _ = Target.saveInstanceContext();
|
|
|
|
// Walk through the path backwards.
|
|
AccessSpecifier PathAccess = FinalAccess;
|
|
CXXBasePath::iterator I = PI->end(), E = PI->begin();
|
|
while (I != E) {
|
|
--I;
|
|
|
|
assert(PathAccess != AS_none);
|
|
|
|
// If the declaration is a private member of a base class, there
|
|
// is no level of friendship in derived classes that can make it
|
|
// accessible.
|
|
if (PathAccess == AS_private) {
|
|
PathAccess = AS_none;
|
|
break;
|
|
}
|
|
|
|
const CXXRecordDecl *NC = I->Class->getCanonicalDecl();
|
|
|
|
AccessSpecifier BaseAccess = I->Base->getAccessSpecifier();
|
|
PathAccess = std::max(PathAccess, BaseAccess);
|
|
|
|
switch (HasAccess(S, EC, NC, PathAccess, Target)) {
|
|
case AR_inaccessible: break;
|
|
case AR_accessible:
|
|
PathAccess = AS_public;
|
|
|
|
// Future tests are not against members and so do not have
|
|
// instance context.
|
|
Target.suppressInstanceContext();
|
|
break;
|
|
case AR_dependent:
|
|
AnyDependent = true;
|
|
goto Next;
|
|
}
|
|
}
|
|
|
|
// Note that we modify the path's Access field to the
|
|
// friend-modified access.
|
|
if (BestPath == nullptr || PathAccess < BestPath->Access) {
|
|
BestPath = &*PI;
|
|
BestPath->Access = PathAccess;
|
|
|
|
// Short-circuit if we found a public path.
|
|
if (BestPath->Access == AS_public)
|
|
return BestPath;
|
|
}
|
|
|
|
Next: ;
|
|
}
|
|
|
|
assert((!BestPath || BestPath->Access != AS_public) &&
|
|
"fell out of loop with public path");
|
|
|
|
// We didn't find a public path, but at least one path was subject
|
|
// to dependent friendship, so delay the check.
|
|
if (AnyDependent)
|
|
return nullptr;
|
|
|
|
return BestPath;
|
|
}
|
|
|
|
/// Given that an entity has protected natural access, check whether
|
|
/// access might be denied because of the protected member access
|
|
/// restriction.
|
|
///
|
|
/// \return true if a note was emitted
|
|
static bool TryDiagnoseProtectedAccess(Sema &S, const EffectiveContext &EC,
|
|
AccessTarget &Target) {
|
|
// Only applies to instance accesses.
|
|
if (!Target.isInstanceMember())
|
|
return false;
|
|
|
|
assert(Target.isMemberAccess());
|
|
|
|
const CXXRecordDecl *NamingClass = Target.getEffectiveNamingClass();
|
|
|
|
for (EffectiveContext::record_iterator
|
|
I = EC.Records.begin(), E = EC.Records.end(); I != E; ++I) {
|
|
const CXXRecordDecl *ECRecord = *I;
|
|
switch (IsDerivedFromInclusive(ECRecord, NamingClass)) {
|
|
case AR_accessible: break;
|
|
case AR_inaccessible: continue;
|
|
case AR_dependent: continue;
|
|
}
|
|
|
|
// The effective context is a subclass of the declaring class.
|
|
// Check whether the [class.protected] restriction is limiting
|
|
// access.
|
|
|
|
// To get this exactly right, this might need to be checked more
|
|
// holistically; it's not necessarily the case that gaining
|
|
// access here would grant us access overall.
|
|
|
|
NamedDecl *D = Target.getTargetDecl();
|
|
|
|
// If we don't have an instance context, [class.protected] says the
|
|
// naming class has to equal the context class.
|
|
if (!Target.hasInstanceContext()) {
|
|
// If it does, the restriction doesn't apply.
|
|
if (NamingClass == ECRecord) continue;
|
|
|
|
// TODO: it would be great to have a fixit here, since this is
|
|
// such an obvious error.
|
|
S.Diag(D->getLocation(), diag::note_access_protected_restricted_noobject)
|
|
<< S.Context.getTypeDeclType(ECRecord);
|
|
return true;
|
|
}
|
|
|
|
const CXXRecordDecl *InstanceContext = Target.resolveInstanceContext(S);
|
|
assert(InstanceContext && "diagnosing dependent access");
|
|
|
|
switch (IsDerivedFromInclusive(InstanceContext, ECRecord)) {
|
|
case AR_accessible: continue;
|
|
case AR_dependent: continue;
|
|
case AR_inaccessible:
|
|
break;
|
|
}
|
|
|
|
// Okay, the restriction seems to be what's limiting us.
|
|
|
|
// Use a special diagnostic for constructors and destructors.
|
|
if (isa<CXXConstructorDecl>(D) || isa<CXXDestructorDecl>(D) ||
|
|
(isa<FunctionTemplateDecl>(D) &&
|
|
isa<CXXConstructorDecl>(
|
|
cast<FunctionTemplateDecl>(D)->getTemplatedDecl()))) {
|
|
return S.Diag(D->getLocation(),
|
|
diag::note_access_protected_restricted_ctordtor)
|
|
<< isa<CXXDestructorDecl>(D->getAsFunction());
|
|
}
|
|
|
|
// Otherwise, use the generic diagnostic.
|
|
return S.Diag(D->getLocation(),
|
|
diag::note_access_protected_restricted_object)
|
|
<< S.Context.getTypeDeclType(ECRecord);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// We are unable to access a given declaration due to its direct
|
|
/// access control; diagnose that.
|
|
static void diagnoseBadDirectAccess(Sema &S,
|
|
const EffectiveContext &EC,
|
|
AccessTarget &entity) {
|
|
assert(entity.isMemberAccess());
|
|
NamedDecl *D = entity.getTargetDecl();
|
|
|
|
if (D->getAccess() == AS_protected &&
|
|
TryDiagnoseProtectedAccess(S, EC, entity))
|
|
return;
|
|
|
|
// Find an original declaration.
|
|
while (D->isOutOfLine()) {
|
|
NamedDecl *PrevDecl = nullptr;
|
|
if (VarDecl *VD = dyn_cast<VarDecl>(D))
|
|
PrevDecl = VD->getPreviousDecl();
|
|
else if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D))
|
|
PrevDecl = FD->getPreviousDecl();
|
|
else if (TypedefNameDecl *TND = dyn_cast<TypedefNameDecl>(D))
|
|
PrevDecl = TND->getPreviousDecl();
|
|
else if (TagDecl *TD = dyn_cast<TagDecl>(D)) {
|
|
if (isa<RecordDecl>(D) && cast<RecordDecl>(D)->isInjectedClassName())
|
|
break;
|
|
PrevDecl = TD->getPreviousDecl();
|
|
}
|
|
if (!PrevDecl) break;
|
|
D = PrevDecl;
|
|
}
|
|
|
|
CXXRecordDecl *DeclaringClass = FindDeclaringClass(D);
|
|
Decl *ImmediateChild;
|
|
if (D->getDeclContext() == DeclaringClass)
|
|
ImmediateChild = D;
|
|
else {
|
|
DeclContext *DC = D->getDeclContext();
|
|
while (DC->getParent() != DeclaringClass)
|
|
DC = DC->getParent();
|
|
ImmediateChild = cast<Decl>(DC);
|
|
}
|
|
|
|
// Check whether there's an AccessSpecDecl preceding this in the
|
|
// chain of the DeclContext.
|
|
bool isImplicit = true;
|
|
for (const auto *I : DeclaringClass->decls()) {
|
|
if (I == ImmediateChild) break;
|
|
if (isa<AccessSpecDecl>(I)) {
|
|
isImplicit = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
S.Diag(D->getLocation(), diag::note_access_natural)
|
|
<< (unsigned) (D->getAccess() == AS_protected)
|
|
<< isImplicit;
|
|
}
|
|
|
|
/// Diagnose the path which caused the given declaration or base class
|
|
/// to become inaccessible.
|
|
static void DiagnoseAccessPath(Sema &S,
|
|
const EffectiveContext &EC,
|
|
AccessTarget &entity) {
|
|
// Save the instance context to preserve invariants.
|
|
AccessTarget::SavedInstanceContext _ = entity.saveInstanceContext();
|
|
|
|
// This basically repeats the main algorithm but keeps some more
|
|
// information.
|
|
|
|
// The natural access so far.
|
|
AccessSpecifier accessSoFar = AS_public;
|
|
|
|
// Check whether we have special rights to the declaring class.
|
|
if (entity.isMemberAccess()) {
|
|
NamedDecl *D = entity.getTargetDecl();
|
|
accessSoFar = D->getAccess();
|
|
const CXXRecordDecl *declaringClass = entity.getDeclaringClass();
|
|
|
|
switch (HasAccess(S, EC, declaringClass, accessSoFar, entity)) {
|
|
// If the declaration is accessible when named in its declaring
|
|
// class, then we must be constrained by the path.
|
|
case AR_accessible:
|
|
accessSoFar = AS_public;
|
|
entity.suppressInstanceContext();
|
|
break;
|
|
|
|
case AR_inaccessible:
|
|
if (accessSoFar == AS_private ||
|
|
declaringClass == entity.getEffectiveNamingClass())
|
|
return diagnoseBadDirectAccess(S, EC, entity);
|
|
break;
|
|
|
|
case AR_dependent:
|
|
llvm_unreachable("cannot diagnose dependent access");
|
|
}
|
|
}
|
|
|
|
CXXBasePaths paths;
|
|
CXXBasePath &path = *FindBestPath(S, EC, entity, accessSoFar, paths);
|
|
assert(path.Access != AS_public);
|
|
|
|
CXXBasePath::iterator i = path.end(), e = path.begin();
|
|
CXXBasePath::iterator constrainingBase = i;
|
|
while (i != e) {
|
|
--i;
|
|
|
|
assert(accessSoFar != AS_none && accessSoFar != AS_private);
|
|
|
|
// Is the entity accessible when named in the deriving class, as
|
|
// modified by the base specifier?
|
|
const CXXRecordDecl *derivingClass = i->Class->getCanonicalDecl();
|
|
const CXXBaseSpecifier *base = i->Base;
|
|
|
|
// If the access to this base is worse than the access we have to
|
|
// the declaration, remember it.
|
|
AccessSpecifier baseAccess = base->getAccessSpecifier();
|
|
if (baseAccess > accessSoFar) {
|
|
constrainingBase = i;
|
|
accessSoFar = baseAccess;
|
|
}
|
|
|
|
switch (HasAccess(S, EC, derivingClass, accessSoFar, entity)) {
|
|
case AR_inaccessible: break;
|
|
case AR_accessible:
|
|
accessSoFar = AS_public;
|
|
entity.suppressInstanceContext();
|
|
constrainingBase = nullptr;
|
|
break;
|
|
case AR_dependent:
|
|
llvm_unreachable("cannot diagnose dependent access");
|
|
}
|
|
|
|
// If this was private inheritance, but we don't have access to
|
|
// the deriving class, we're done.
|
|
if (accessSoFar == AS_private) {
|
|
assert(baseAccess == AS_private);
|
|
assert(constrainingBase == i);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If we don't have a constraining base, the access failure must be
|
|
// due to the original declaration.
|
|
if (constrainingBase == path.end())
|
|
return diagnoseBadDirectAccess(S, EC, entity);
|
|
|
|
// We're constrained by inheritance, but we want to say
|
|
// "declared private here" if we're diagnosing a hierarchy
|
|
// conversion and this is the final step.
|
|
unsigned diagnostic;
|
|
if (entity.isMemberAccess() ||
|
|
constrainingBase + 1 != path.end()) {
|
|
diagnostic = diag::note_access_constrained_by_path;
|
|
} else {
|
|
diagnostic = diag::note_access_natural;
|
|
}
|
|
|
|
const CXXBaseSpecifier *base = constrainingBase->Base;
|
|
|
|
S.Diag(base->getSourceRange().getBegin(), diagnostic)
|
|
<< base->getSourceRange()
|
|
<< (base->getAccessSpecifier() == AS_protected)
|
|
<< (base->getAccessSpecifierAsWritten() == AS_none);
|
|
|
|
if (entity.isMemberAccess())
|
|
S.Diag(entity.getTargetDecl()->getLocation(),
|
|
diag::note_member_declared_at);
|
|
}
|
|
|
|
static void DiagnoseBadAccess(Sema &S, SourceLocation Loc,
|
|
const EffectiveContext &EC,
|
|
AccessTarget &Entity) {
|
|
const CXXRecordDecl *NamingClass = Entity.getNamingClass();
|
|
const CXXRecordDecl *DeclaringClass = Entity.getDeclaringClass();
|
|
NamedDecl *D = (Entity.isMemberAccess() ? Entity.getTargetDecl() : nullptr);
|
|
|
|
S.Diag(Loc, Entity.getDiag())
|
|
<< (Entity.getAccess() == AS_protected)
|
|
<< (D ? D->getDeclName() : DeclarationName())
|
|
<< S.Context.getTypeDeclType(NamingClass)
|
|
<< S.Context.getTypeDeclType(DeclaringClass);
|
|
DiagnoseAccessPath(S, EC, Entity);
|
|
}
|
|
|
|
/// MSVC has a bug where if during an using declaration name lookup,
|
|
/// the declaration found is unaccessible (private) and that declaration
|
|
/// was bring into scope via another using declaration whose target
|
|
/// declaration is accessible (public) then no error is generated.
|
|
/// Example:
|
|
/// class A {
|
|
/// public:
|
|
/// int f();
|
|
/// };
|
|
/// class B : public A {
|
|
/// private:
|
|
/// using A::f;
|
|
/// };
|
|
/// class C : public B {
|
|
/// private:
|
|
/// using B::f;
|
|
/// };
|
|
///
|
|
/// Here, B::f is private so this should fail in Standard C++, but
|
|
/// because B::f refers to A::f which is public MSVC accepts it.
|
|
static bool IsMicrosoftUsingDeclarationAccessBug(Sema& S,
|
|
SourceLocation AccessLoc,
|
|
AccessTarget &Entity) {
|
|
if (UsingShadowDecl *Shadow =
|
|
dyn_cast<UsingShadowDecl>(Entity.getTargetDecl()))
|
|
if (UsingDecl *UD = dyn_cast<UsingDecl>(Shadow->getIntroducer())) {
|
|
const NamedDecl *OrigDecl = Entity.getTargetDecl()->getUnderlyingDecl();
|
|
if (Entity.getTargetDecl()->getAccess() == AS_private &&
|
|
(OrigDecl->getAccess() == AS_public ||
|
|
OrigDecl->getAccess() == AS_protected)) {
|
|
S.Diag(AccessLoc, diag::ext_ms_using_declaration_inaccessible)
|
|
<< UD->getQualifiedNameAsString()
|
|
<< OrigDecl->getQualifiedNameAsString();
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// Determines whether the accessed entity is accessible. Public members
|
|
/// have been weeded out by this point.
|
|
static AccessResult IsAccessible(Sema &S,
|
|
const EffectiveContext &EC,
|
|
AccessTarget &Entity) {
|
|
// Determine the actual naming class.
|
|
const CXXRecordDecl *NamingClass = Entity.getEffectiveNamingClass();
|
|
|
|
AccessSpecifier UnprivilegedAccess = Entity.getAccess();
|
|
assert(UnprivilegedAccess != AS_public && "public access not weeded out");
|
|
|
|
// Before we try to recalculate access paths, try to white-list
|
|
// accesses which just trade in on the final step, i.e. accesses
|
|
// which don't require [M4] or [B4]. These are by far the most
|
|
// common forms of privileged access.
|
|
if (UnprivilegedAccess != AS_none) {
|
|
switch (HasAccess(S, EC, NamingClass, UnprivilegedAccess, Entity)) {
|
|
case AR_dependent:
|
|
// This is actually an interesting policy decision. We don't
|
|
// *have* to delay immediately here: we can do the full access
|
|
// calculation in the hope that friendship on some intermediate
|
|
// class will make the declaration accessible non-dependently.
|
|
// But that's not cheap, and odds are very good (note: assertion
|
|
// made without data) that the friend declaration will determine
|
|
// access.
|
|
return AR_dependent;
|
|
|
|
case AR_accessible: return AR_accessible;
|
|
case AR_inaccessible: break;
|
|
}
|
|
}
|
|
|
|
AccessTarget::SavedInstanceContext _ = Entity.saveInstanceContext();
|
|
|
|
// We lower member accesses to base accesses by pretending that the
|
|
// member is a base class of its declaring class.
|
|
AccessSpecifier FinalAccess;
|
|
|
|
if (Entity.isMemberAccess()) {
|
|
// Determine if the declaration is accessible from EC when named
|
|
// in its declaring class.
|
|
NamedDecl *Target = Entity.getTargetDecl();
|
|
const CXXRecordDecl *DeclaringClass = Entity.getDeclaringClass();
|
|
|
|
FinalAccess = Target->getAccess();
|
|
switch (HasAccess(S, EC, DeclaringClass, FinalAccess, Entity)) {
|
|
case AR_accessible:
|
|
// Target is accessible at EC when named in its declaring class.
|
|
// We can now hill-climb and simply check whether the declaring
|
|
// class is accessible as a base of the naming class. This is
|
|
// equivalent to checking the access of a notional public
|
|
// member with no instance context.
|
|
FinalAccess = AS_public;
|
|
Entity.suppressInstanceContext();
|
|
break;
|
|
case AR_inaccessible: break;
|
|
case AR_dependent: return AR_dependent; // see above
|
|
}
|
|
|
|
if (DeclaringClass == NamingClass)
|
|
return (FinalAccess == AS_public ? AR_accessible : AR_inaccessible);
|
|
} else {
|
|
FinalAccess = AS_public;
|
|
}
|
|
|
|
assert(Entity.getDeclaringClass() != NamingClass);
|
|
|
|
// Append the declaration's access if applicable.
|
|
CXXBasePaths Paths;
|
|
CXXBasePath *Path = FindBestPath(S, EC, Entity, FinalAccess, Paths);
|
|
if (!Path)
|
|
return AR_dependent;
|
|
|
|
assert(Path->Access <= UnprivilegedAccess &&
|
|
"access along best path worse than direct?");
|
|
if (Path->Access == AS_public)
|
|
return AR_accessible;
|
|
return AR_inaccessible;
|
|
}
|
|
|
|
static void DelayDependentAccess(Sema &S,
|
|
const EffectiveContext &EC,
|
|
SourceLocation Loc,
|
|
const AccessTarget &Entity) {
|
|
assert(EC.isDependent() && "delaying non-dependent access");
|
|
DeclContext *DC = EC.getInnerContext();
|
|
assert(DC->isDependentContext() && "delaying non-dependent access");
|
|
DependentDiagnostic::Create(S.Context, DC, DependentDiagnostic::Access,
|
|
Loc,
|
|
Entity.isMemberAccess(),
|
|
Entity.getAccess(),
|
|
Entity.getTargetDecl(),
|
|
Entity.getNamingClass(),
|
|
Entity.getBaseObjectType(),
|
|
Entity.getDiag());
|
|
}
|
|
|
|
/// Checks access to an entity from the given effective context.
|
|
static AccessResult CheckEffectiveAccess(Sema &S,
|
|
const EffectiveContext &EC,
|
|
SourceLocation Loc,
|
|
AccessTarget &Entity) {
|
|
assert(Entity.getAccess() != AS_public && "called for public access!");
|
|
|
|
switch (IsAccessible(S, EC, Entity)) {
|
|
case AR_dependent:
|
|
DelayDependentAccess(S, EC, Loc, Entity);
|
|
return AR_dependent;
|
|
|
|
case AR_inaccessible:
|
|
if (S.getLangOpts().MSVCCompat &&
|
|
IsMicrosoftUsingDeclarationAccessBug(S, Loc, Entity))
|
|
return AR_accessible;
|
|
if (!Entity.isQuiet())
|
|
DiagnoseBadAccess(S, Loc, EC, Entity);
|
|
return AR_inaccessible;
|
|
|
|
case AR_accessible:
|
|
return AR_accessible;
|
|
}
|
|
|
|
// silence unnecessary warning
|
|
llvm_unreachable("invalid access result");
|
|
}
|
|
|
|
static Sema::AccessResult CheckAccess(Sema &S, SourceLocation Loc,
|
|
AccessTarget &Entity) {
|
|
// If the access path is public, it's accessible everywhere.
|
|
if (Entity.getAccess() == AS_public)
|
|
return Sema::AR_accessible;
|
|
|
|
// If we're currently parsing a declaration, we may need to delay
|
|
// access control checking, because our effective context might be
|
|
// different based on what the declaration comes out as.
|
|
//
|
|
// For example, we might be parsing a declaration with a scope
|
|
// specifier, like this:
|
|
// A::private_type A::foo() { ... }
|
|
//
|
|
// Or we might be parsing something that will turn out to be a friend:
|
|
// void foo(A::private_type);
|
|
// void B::foo(A::private_type);
|
|
if (S.DelayedDiagnostics.shouldDelayDiagnostics()) {
|
|
S.DelayedDiagnostics.add(DelayedDiagnostic::makeAccess(Loc, Entity));
|
|
return Sema::AR_delayed;
|
|
}
|
|
|
|
EffectiveContext EC(S.CurContext);
|
|
switch (CheckEffectiveAccess(S, EC, Loc, Entity)) {
|
|
case AR_accessible: return Sema::AR_accessible;
|
|
case AR_inaccessible: return Sema::AR_inaccessible;
|
|
case AR_dependent: return Sema::AR_dependent;
|
|
}
|
|
llvm_unreachable("invalid access result");
|
|
}
|
|
|
|
void Sema::HandleDelayedAccessCheck(DelayedDiagnostic &DD, Decl *D) {
|
|
// Access control for names used in the declarations of functions
|
|
// and function templates should normally be evaluated in the context
|
|
// of the declaration, just in case it's a friend of something.
|
|
// However, this does not apply to local extern declarations.
|
|
|
|
DeclContext *DC = D->getDeclContext();
|
|
if (D->isLocalExternDecl()) {
|
|
DC = D->getLexicalDeclContext();
|
|
} else if (FunctionDecl *FN = dyn_cast<FunctionDecl>(D)) {
|
|
DC = FN;
|
|
} else if (TemplateDecl *TD = dyn_cast<TemplateDecl>(D)) {
|
|
if (isa<DeclContext>(TD->getTemplatedDecl()))
|
|
DC = cast<DeclContext>(TD->getTemplatedDecl());
|
|
}
|
|
|
|
EffectiveContext EC(DC);
|
|
|
|
AccessTarget Target(DD.getAccessData());
|
|
|
|
if (CheckEffectiveAccess(*this, EC, DD.Loc, Target) == ::AR_inaccessible)
|
|
DD.Triggered = true;
|
|
}
|
|
|
|
void Sema::HandleDependentAccessCheck(const DependentDiagnostic &DD,
|
|
const MultiLevelTemplateArgumentList &TemplateArgs) {
|
|
SourceLocation Loc = DD.getAccessLoc();
|
|
AccessSpecifier Access = DD.getAccess();
|
|
|
|
Decl *NamingD = FindInstantiatedDecl(Loc, DD.getAccessNamingClass(),
|
|
TemplateArgs);
|
|
if (!NamingD) return;
|
|
Decl *TargetD = FindInstantiatedDecl(Loc, DD.getAccessTarget(),
|
|
TemplateArgs);
|
|
if (!TargetD) return;
|
|
|
|
if (DD.isAccessToMember()) {
|
|
CXXRecordDecl *NamingClass = cast<CXXRecordDecl>(NamingD);
|
|
NamedDecl *TargetDecl = cast<NamedDecl>(TargetD);
|
|
QualType BaseObjectType = DD.getAccessBaseObjectType();
|
|
if (!BaseObjectType.isNull()) {
|
|
BaseObjectType = SubstType(BaseObjectType, TemplateArgs, Loc,
|
|
DeclarationName());
|
|
if (BaseObjectType.isNull()) return;
|
|
}
|
|
|
|
AccessTarget Entity(Context,
|
|
AccessTarget::Member,
|
|
NamingClass,
|
|
DeclAccessPair::make(TargetDecl, Access),
|
|
BaseObjectType);
|
|
Entity.setDiag(DD.getDiagnostic());
|
|
CheckAccess(*this, Loc, Entity);
|
|
} else {
|
|
AccessTarget Entity(Context,
|
|
AccessTarget::Base,
|
|
cast<CXXRecordDecl>(TargetD),
|
|
cast<CXXRecordDecl>(NamingD),
|
|
Access);
|
|
Entity.setDiag(DD.getDiagnostic());
|
|
CheckAccess(*this, Loc, Entity);
|
|
}
|
|
}
|
|
|
|
Sema::AccessResult Sema::CheckUnresolvedLookupAccess(UnresolvedLookupExpr *E,
|
|
DeclAccessPair Found) {
|
|
if (!getLangOpts().AccessControl ||
|
|
!E->getNamingClass() ||
|
|
Found.getAccess() == AS_public)
|
|
return AR_accessible;
|
|
|
|
AccessTarget Entity(Context, AccessTarget::Member, E->getNamingClass(),
|
|
Found, QualType());
|
|
Entity.setDiag(diag::err_access) << E->getSourceRange();
|
|
|
|
return CheckAccess(*this, E->getNameLoc(), Entity);
|
|
}
|
|
|
|
/// Perform access-control checking on a previously-unresolved member
|
|
/// access which has now been resolved to a member.
|
|
Sema::AccessResult Sema::CheckUnresolvedMemberAccess(UnresolvedMemberExpr *E,
|
|
DeclAccessPair Found) {
|
|
if (!getLangOpts().AccessControl ||
|
|
Found.getAccess() == AS_public)
|
|
return AR_accessible;
|
|
|
|
QualType BaseType = E->getBaseType();
|
|
if (E->isArrow())
|
|
BaseType = BaseType->castAs<PointerType>()->getPointeeType();
|
|
|
|
AccessTarget Entity(Context, AccessTarget::Member, E->getNamingClass(),
|
|
Found, BaseType);
|
|
Entity.setDiag(diag::err_access) << E->getSourceRange();
|
|
|
|
return CheckAccess(*this, E->getMemberLoc(), Entity);
|
|
}
|
|
|
|
/// Is the given member accessible for the purposes of deciding whether to
|
|
/// define a special member function as deleted?
|
|
bool Sema::isMemberAccessibleForDeletion(CXXRecordDecl *NamingClass,
|
|
DeclAccessPair Found,
|
|
QualType ObjectType,
|
|
SourceLocation Loc,
|
|
const PartialDiagnostic &Diag) {
|
|
// Fast path.
|
|
if (Found.getAccess() == AS_public || !getLangOpts().AccessControl)
|
|
return true;
|
|
|
|
AccessTarget Entity(Context, AccessTarget::Member, NamingClass, Found,
|
|
ObjectType);
|
|
|
|
// Suppress diagnostics.
|
|
Entity.setDiag(Diag);
|
|
|
|
switch (CheckAccess(*this, Loc, Entity)) {
|
|
case AR_accessible: return true;
|
|
case AR_inaccessible: return false;
|
|
case AR_dependent: llvm_unreachable("dependent for =delete computation");
|
|
case AR_delayed: llvm_unreachable("cannot delay =delete computation");
|
|
}
|
|
llvm_unreachable("bad access result");
|
|
}
|
|
|
|
Sema::AccessResult Sema::CheckDestructorAccess(SourceLocation Loc,
|
|
CXXDestructorDecl *Dtor,
|
|
const PartialDiagnostic &PDiag,
|
|
QualType ObjectTy) {
|
|
if (!getLangOpts().AccessControl)
|
|
return AR_accessible;
|
|
|
|
// There's never a path involved when checking implicit destructor access.
|
|
AccessSpecifier Access = Dtor->getAccess();
|
|
if (Access == AS_public)
|
|
return AR_accessible;
|
|
|
|
CXXRecordDecl *NamingClass = Dtor->getParent();
|
|
if (ObjectTy.isNull()) ObjectTy = Context.getTypeDeclType(NamingClass);
|
|
|
|
AccessTarget Entity(Context, AccessTarget::Member, NamingClass,
|
|
DeclAccessPair::make(Dtor, Access),
|
|
ObjectTy);
|
|
Entity.setDiag(PDiag); // TODO: avoid copy
|
|
|
|
return CheckAccess(*this, Loc, Entity);
|
|
}
|
|
|
|
/// Checks access to a constructor.
|
|
Sema::AccessResult Sema::CheckConstructorAccess(SourceLocation UseLoc,
|
|
CXXConstructorDecl *Constructor,
|
|
DeclAccessPair Found,
|
|
const InitializedEntity &Entity,
|
|
bool IsCopyBindingRefToTemp) {
|
|
if (!getLangOpts().AccessControl || Found.getAccess() == AS_public)
|
|
return AR_accessible;
|
|
|
|
PartialDiagnostic PD(PDiag());
|
|
switch (Entity.getKind()) {
|
|
default:
|
|
PD = PDiag(IsCopyBindingRefToTemp
|
|
? diag::ext_rvalue_to_reference_access_ctor
|
|
: diag::err_access_ctor);
|
|
|
|
break;
|
|
|
|
case InitializedEntity::EK_Base:
|
|
PD = PDiag(diag::err_access_base_ctor);
|
|
PD << Entity.isInheritedVirtualBase()
|
|
<< Entity.getBaseSpecifier()->getType() << getSpecialMember(Constructor);
|
|
break;
|
|
|
|
case InitializedEntity::EK_Member: {
|
|
const FieldDecl *Field = cast<FieldDecl>(Entity.getDecl());
|
|
PD = PDiag(diag::err_access_field_ctor);
|
|
PD << Field->getType() << getSpecialMember(Constructor);
|
|
break;
|
|
}
|
|
|
|
case InitializedEntity::EK_LambdaCapture: {
|
|
StringRef VarName = Entity.getCapturedVarName();
|
|
PD = PDiag(diag::err_access_lambda_capture);
|
|
PD << VarName << Entity.getType() << getSpecialMember(Constructor);
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
return CheckConstructorAccess(UseLoc, Constructor, Found, Entity, PD);
|
|
}
|
|
|
|
/// Checks access to a constructor.
|
|
Sema::AccessResult Sema::CheckConstructorAccess(SourceLocation UseLoc,
|
|
CXXConstructorDecl *Constructor,
|
|
DeclAccessPair Found,
|
|
const InitializedEntity &Entity,
|
|
const PartialDiagnostic &PD) {
|
|
if (!getLangOpts().AccessControl ||
|
|
Found.getAccess() == AS_public)
|
|
return AR_accessible;
|
|
|
|
CXXRecordDecl *NamingClass = Constructor->getParent();
|
|
|
|
// Initializing a base sub-object is an instance method call on an
|
|
// object of the derived class. Otherwise, we have an instance method
|
|
// call on an object of the constructed type.
|
|
//
|
|
// FIXME: If we have a parent, we're initializing the base class subobject
|
|
// in aggregate initialization. It's not clear whether the object class
|
|
// should be the base class or the derived class in that case.
|
|
CXXRecordDecl *ObjectClass;
|
|
if ((Entity.getKind() == InitializedEntity::EK_Base ||
|
|
Entity.getKind() == InitializedEntity::EK_Delegating) &&
|
|
!Entity.getParent()) {
|
|
ObjectClass = cast<CXXConstructorDecl>(CurContext)->getParent();
|
|
} else if (auto *Shadow =
|
|
dyn_cast<ConstructorUsingShadowDecl>(Found.getDecl())) {
|
|
// If we're using an inheriting constructor to construct an object,
|
|
// the object class is the derived class, not the base class.
|
|
ObjectClass = Shadow->getParent();
|
|
} else {
|
|
ObjectClass = NamingClass;
|
|
}
|
|
|
|
AccessTarget AccessEntity(
|
|
Context, AccessTarget::Member, NamingClass,
|
|
DeclAccessPair::make(Constructor, Found.getAccess()),
|
|
Context.getTypeDeclType(ObjectClass));
|
|
AccessEntity.setDiag(PD);
|
|
|
|
return CheckAccess(*this, UseLoc, AccessEntity);
|
|
}
|
|
|
|
/// Checks access to an overloaded operator new or delete.
|
|
Sema::AccessResult Sema::CheckAllocationAccess(SourceLocation OpLoc,
|
|
SourceRange PlacementRange,
|
|
CXXRecordDecl *NamingClass,
|
|
DeclAccessPair Found,
|
|
bool Diagnose) {
|
|
if (!getLangOpts().AccessControl ||
|
|
!NamingClass ||
|
|
Found.getAccess() == AS_public)
|
|
return AR_accessible;
|
|
|
|
AccessTarget Entity(Context, AccessTarget::Member, NamingClass, Found,
|
|
QualType());
|
|
if (Diagnose)
|
|
Entity.setDiag(diag::err_access)
|
|
<< PlacementRange;
|
|
|
|
return CheckAccess(*this, OpLoc, Entity);
|
|
}
|
|
|
|
/// Checks access to a member.
|
|
Sema::AccessResult Sema::CheckMemberAccess(SourceLocation UseLoc,
|
|
CXXRecordDecl *NamingClass,
|
|
DeclAccessPair Found) {
|
|
if (!getLangOpts().AccessControl ||
|
|
!NamingClass ||
|
|
Found.getAccess() == AS_public)
|
|
return AR_accessible;
|
|
|
|
AccessTarget Entity(Context, AccessTarget::Member, NamingClass,
|
|
Found, QualType());
|
|
|
|
return CheckAccess(*this, UseLoc, Entity);
|
|
}
|
|
|
|
/// Checks implicit access to a member in a structured binding.
|
|
Sema::AccessResult
|
|
Sema::CheckStructuredBindingMemberAccess(SourceLocation UseLoc,
|
|
CXXRecordDecl *DecomposedClass,
|
|
DeclAccessPair Field) {
|
|
if (!getLangOpts().AccessControl ||
|
|
Field.getAccess() == AS_public)
|
|
return AR_accessible;
|
|
|
|
AccessTarget Entity(Context, AccessTarget::Member, DecomposedClass, Field,
|
|
Context.getRecordType(DecomposedClass));
|
|
Entity.setDiag(diag::err_decomp_decl_inaccessible_field);
|
|
|
|
return CheckAccess(*this, UseLoc, Entity);
|
|
}
|
|
|
|
/// Checks access to an overloaded member operator, including
|
|
/// conversion operators.
|
|
Sema::AccessResult Sema::CheckMemberOperatorAccess(SourceLocation OpLoc,
|
|
Expr *ObjectExpr,
|
|
Expr *ArgExpr,
|
|
DeclAccessPair Found) {
|
|
if (!getLangOpts().AccessControl ||
|
|
Found.getAccess() == AS_public)
|
|
return AR_accessible;
|
|
|
|
const RecordType *RT = ObjectExpr->getType()->castAs<RecordType>();
|
|
CXXRecordDecl *NamingClass = cast<CXXRecordDecl>(RT->getDecl());
|
|
|
|
AccessTarget Entity(Context, AccessTarget::Member, NamingClass, Found,
|
|
ObjectExpr->getType());
|
|
Entity.setDiag(diag::err_access)
|
|
<< ObjectExpr->getSourceRange()
|
|
<< (ArgExpr ? ArgExpr->getSourceRange() : SourceRange());
|
|
|
|
return CheckAccess(*this, OpLoc, Entity);
|
|
}
|
|
|
|
/// Checks access to the target of a friend declaration.
|
|
Sema::AccessResult Sema::CheckFriendAccess(NamedDecl *target) {
|
|
assert(isa<CXXMethodDecl>(target->getAsFunction()));
|
|
|
|
// Friendship lookup is a redeclaration lookup, so there's never an
|
|
// inheritance path modifying access.
|
|
AccessSpecifier access = target->getAccess();
|
|
|
|
if (!getLangOpts().AccessControl || access == AS_public)
|
|
return AR_accessible;
|
|
|
|
CXXMethodDecl *method = cast<CXXMethodDecl>(target->getAsFunction());
|
|
|
|
AccessTarget entity(Context, AccessTarget::Member,
|
|
cast<CXXRecordDecl>(target->getDeclContext()),
|
|
DeclAccessPair::make(target, access),
|
|
/*no instance context*/ QualType());
|
|
entity.setDiag(diag::err_access_friend_function)
|
|
<< (method->getQualifier() ? method->getQualifierLoc().getSourceRange()
|
|
: method->getNameInfo().getSourceRange());
|
|
|
|
// We need to bypass delayed-diagnostics because we might be called
|
|
// while the ParsingDeclarator is active.
|
|
EffectiveContext EC(CurContext);
|
|
switch (CheckEffectiveAccess(*this, EC, target->getLocation(), entity)) {
|
|
case ::AR_accessible: return Sema::AR_accessible;
|
|
case ::AR_inaccessible: return Sema::AR_inaccessible;
|
|
case ::AR_dependent: return Sema::AR_dependent;
|
|
}
|
|
llvm_unreachable("invalid access result");
|
|
}
|
|
|
|
Sema::AccessResult Sema::CheckAddressOfMemberAccess(Expr *OvlExpr,
|
|
DeclAccessPair Found) {
|
|
if (!getLangOpts().AccessControl ||
|
|
Found.getAccess() == AS_none ||
|
|
Found.getAccess() == AS_public)
|
|
return AR_accessible;
|
|
|
|
OverloadExpr *Ovl = OverloadExpr::find(OvlExpr).Expression;
|
|
CXXRecordDecl *NamingClass = Ovl->getNamingClass();
|
|
|
|
AccessTarget Entity(Context, AccessTarget::Member, NamingClass, Found,
|
|
/*no instance context*/ QualType());
|
|
Entity.setDiag(diag::err_access)
|
|
<< Ovl->getSourceRange();
|
|
|
|
return CheckAccess(*this, Ovl->getNameLoc(), Entity);
|
|
}
|
|
|
|
/// Checks access for a hierarchy conversion.
|
|
///
|
|
/// \param ForceCheck true if this check should be performed even if access
|
|
/// control is disabled; some things rely on this for semantics
|
|
/// \param ForceUnprivileged true if this check should proceed as if the
|
|
/// context had no special privileges
|
|
Sema::AccessResult Sema::CheckBaseClassAccess(SourceLocation AccessLoc,
|
|
QualType Base,
|
|
QualType Derived,
|
|
const CXXBasePath &Path,
|
|
unsigned DiagID,
|
|
bool ForceCheck,
|
|
bool ForceUnprivileged) {
|
|
if (!ForceCheck && !getLangOpts().AccessControl)
|
|
return AR_accessible;
|
|
|
|
if (Path.Access == AS_public)
|
|
return AR_accessible;
|
|
|
|
CXXRecordDecl *BaseD, *DerivedD;
|
|
BaseD = cast<CXXRecordDecl>(Base->castAs<RecordType>()->getDecl());
|
|
DerivedD = cast<CXXRecordDecl>(Derived->castAs<RecordType>()->getDecl());
|
|
|
|
AccessTarget Entity(Context, AccessTarget::Base, BaseD, DerivedD,
|
|
Path.Access);
|
|
if (DiagID)
|
|
Entity.setDiag(DiagID) << Derived << Base;
|
|
|
|
if (ForceUnprivileged) {
|
|
switch (CheckEffectiveAccess(*this, EffectiveContext(),
|
|
AccessLoc, Entity)) {
|
|
case ::AR_accessible: return Sema::AR_accessible;
|
|
case ::AR_inaccessible: return Sema::AR_inaccessible;
|
|
case ::AR_dependent: return Sema::AR_dependent;
|
|
}
|
|
llvm_unreachable("unexpected result from CheckEffectiveAccess");
|
|
}
|
|
return CheckAccess(*this, AccessLoc, Entity);
|
|
}
|
|
|
|
/// Checks access to all the declarations in the given result set.
|
|
void Sema::CheckLookupAccess(const LookupResult &R) {
|
|
assert(getLangOpts().AccessControl
|
|
&& "performing access check without access control");
|
|
assert(R.getNamingClass() && "performing access check without naming class");
|
|
|
|
for (LookupResult::iterator I = R.begin(), E = R.end(); I != E; ++I) {
|
|
if (I.getAccess() != AS_public) {
|
|
AccessTarget Entity(Context, AccessedEntity::Member,
|
|
R.getNamingClass(), I.getPair(),
|
|
R.getBaseObjectType());
|
|
Entity.setDiag(diag::err_access);
|
|
CheckAccess(*this, R.getNameLoc(), Entity);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Checks access to Target from the given class. The check will take access
|
|
/// specifiers into account, but no member access expressions and such.
|
|
///
|
|
/// \param Target the declaration to check if it can be accessed
|
|
/// \param NamingClass the class in which the lookup was started.
|
|
/// \param BaseType type of the left side of member access expression.
|
|
/// \p BaseType and \p NamingClass are used for C++ access control.
|
|
/// Depending on the lookup case, they should be set to the following:
|
|
/// - lhs.target (member access without a qualifier):
|
|
/// \p BaseType and \p NamingClass are both the type of 'lhs'.
|
|
/// - lhs.X::target (member access with a qualifier):
|
|
/// BaseType is the type of 'lhs', NamingClass is 'X'
|
|
/// - X::target (qualified lookup without member access):
|
|
/// BaseType is null, NamingClass is 'X'.
|
|
/// - target (unqualified lookup).
|
|
/// BaseType is null, NamingClass is the parent class of 'target'.
|
|
/// \return true if the Target is accessible from the Class, false otherwise.
|
|
bool Sema::IsSimplyAccessible(NamedDecl *Target, CXXRecordDecl *NamingClass,
|
|
QualType BaseType) {
|
|
// Perform the C++ accessibility checks first.
|
|
if (Target->isCXXClassMember() && NamingClass) {
|
|
if (!getLangOpts().CPlusPlus)
|
|
return false;
|
|
// The unprivileged access is AS_none as we don't know how the member was
|
|
// accessed, which is described by the access in DeclAccessPair.
|
|
// `IsAccessible` will examine the actual access of Target (i.e.
|
|
// Decl->getAccess()) when calculating the access.
|
|
AccessTarget Entity(Context, AccessedEntity::Member, NamingClass,
|
|
DeclAccessPair::make(Target, AS_none), BaseType);
|
|
EffectiveContext EC(CurContext);
|
|
return ::IsAccessible(*this, EC, Entity) != ::AR_inaccessible;
|
|
}
|
|
|
|
if (ObjCIvarDecl *Ivar = dyn_cast<ObjCIvarDecl>(Target)) {
|
|
// @public and @package ivars are always accessible.
|
|
if (Ivar->getCanonicalAccessControl() == ObjCIvarDecl::Public ||
|
|
Ivar->getCanonicalAccessControl() == ObjCIvarDecl::Package)
|
|
return true;
|
|
|
|
// If we are inside a class or category implementation, determine the
|
|
// interface we're in.
|
|
ObjCInterfaceDecl *ClassOfMethodDecl = nullptr;
|
|
if (ObjCMethodDecl *MD = getCurMethodDecl())
|
|
ClassOfMethodDecl = MD->getClassInterface();
|
|
else if (FunctionDecl *FD = getCurFunctionDecl()) {
|
|
if (ObjCImplDecl *Impl
|
|
= dyn_cast<ObjCImplDecl>(FD->getLexicalDeclContext())) {
|
|
if (ObjCImplementationDecl *IMPD
|
|
= dyn_cast<ObjCImplementationDecl>(Impl))
|
|
ClassOfMethodDecl = IMPD->getClassInterface();
|
|
else if (ObjCCategoryImplDecl* CatImplClass
|
|
= dyn_cast<ObjCCategoryImplDecl>(Impl))
|
|
ClassOfMethodDecl = CatImplClass->getClassInterface();
|
|
}
|
|
}
|
|
|
|
// If we're not in an interface, this ivar is inaccessible.
|
|
if (!ClassOfMethodDecl)
|
|
return false;
|
|
|
|
// If we're inside the same interface that owns the ivar, we're fine.
|
|
if (declaresSameEntity(ClassOfMethodDecl, Ivar->getContainingInterface()))
|
|
return true;
|
|
|
|
// If the ivar is private, it's inaccessible.
|
|
if (Ivar->getCanonicalAccessControl() == ObjCIvarDecl::Private)
|
|
return false;
|
|
|
|
return Ivar->getContainingInterface()->isSuperClassOf(ClassOfMethodDecl);
|
|
}
|
|
|
|
return true;
|
|
}
|