2009-03-27 13:05:05 +08:00
|
|
|
//===---- SemaAccess.cpp - C++ Access Control -------------------*- C++ -*-===//
|
2009-03-27 12:43:36 +08:00
|
|
|
//
|
|
|
|
// The LLVM Compiler Infrastructure
|
|
|
|
//
|
|
|
|
// This file is distributed under the University of Illinois Open Source
|
|
|
|
// License. See LICENSE.TXT for details.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
//
|
|
|
|
// This file provides Sema routines for C++ access control semantics.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
2009-03-27 12:54:36 +08:00
|
|
|
|
|
|
|
#include "Sema.h"
|
2010-01-23 08:46:32 +08:00
|
|
|
#include "Lookup.h"
|
2009-03-27 14:03:27 +08:00
|
|
|
#include "clang/AST/ASTContext.h"
|
2009-10-07 01:59:45 +08:00
|
|
|
#include "clang/AST/CXXInheritance.h"
|
|
|
|
#include "clang/AST/DeclCXX.h"
|
2010-03-12 09:19:31 +08:00
|
|
|
#include "clang/AST/DeclFriend.h"
|
2010-03-24 13:22:00 +08:00
|
|
|
#include "clang/AST/DependentDiagnostic.h"
|
2010-01-27 09:50:18 +08:00
|
|
|
#include "clang/AST/ExprCXX.h"
|
|
|
|
|
2009-03-27 12:54:36 +08:00
|
|
|
using namespace clang;
|
|
|
|
|
2009-03-27 13:05:05 +08:00
|
|
|
/// 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).
|
2009-09-09 23:08:12 +08:00
|
|
|
bool Sema::SetMemberAccessSpecifier(NamedDecl *MemberDecl,
|
2009-03-27 12:54:36 +08:00
|
|
|
NamedDecl *PrevMemberDecl,
|
|
|
|
AccessSpecifier LexicalAS) {
|
|
|
|
if (!PrevMemberDecl) {
|
|
|
|
// Use the lexical access specifier.
|
|
|
|
MemberDecl->setAccess(LexicalAS);
|
|
|
|
return false;
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-03-27 12:54:36 +08:00
|
|
|
// 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()) {
|
2009-09-09 23:08:12 +08:00
|
|
|
Diag(MemberDecl->getLocation(),
|
|
|
|
diag::err_class_redeclared_with_different_access)
|
2009-03-27 12:54:36 +08:00
|
|
|
<< MemberDecl << LexicalAS;
|
|
|
|
Diag(PrevMemberDecl->getLocation(), diag::note_previous_access_declaration)
|
|
|
|
<< PrevMemberDecl << PrevMemberDecl->getAccess();
|
2009-12-23 08:37:40 +08:00
|
|
|
|
|
|
|
MemberDecl->setAccess(LexicalAS);
|
2009-03-27 12:54:36 +08:00
|
|
|
return true;
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-03-27 12:54:36 +08:00
|
|
|
MemberDecl->setAccess(PrevMemberDecl->getAccess());
|
|
|
|
return false;
|
|
|
|
}
|
2009-03-27 13:05:05 +08:00
|
|
|
|
2010-02-10 17:31:12 +08:00
|
|
|
namespace {
|
|
|
|
struct EffectiveContext {
|
2010-03-24 15:46:06 +08:00
|
|
|
EffectiveContext() : Inner(0), Function(0), Dependent(false) {}
|
2010-02-10 17:31:12 +08:00
|
|
|
|
2010-03-24 15:46:06 +08:00
|
|
|
explicit EffectiveContext(DeclContext *DC)
|
|
|
|
: Inner(DC),
|
|
|
|
Dependent(DC->isDependentContext()) {
|
2010-03-24 13:22:00 +08:00
|
|
|
|
2010-02-10 17:31:12 +08:00
|
|
|
if (isa<FunctionDecl>(DC)) {
|
2010-03-12 09:19:31 +08:00
|
|
|
Function = cast<FunctionDecl>(DC)->getCanonicalDecl();
|
2010-02-10 17:31:12 +08:00
|
|
|
DC = Function->getDeclContext();
|
|
|
|
} else
|
|
|
|
Function = 0;
|
2010-03-17 12:58:56 +08:00
|
|
|
|
|
|
|
// C++ [class.access.nest]p1:
|
|
|
|
// A nested class is a member and as such has the same access
|
|
|
|
// rights as any other member.
|
|
|
|
// C++ [class.access]p2:
|
|
|
|
// A member of a class can also access all the names to which
|
|
|
|
// the class has access.
|
|
|
|
// This implies that the privileges of nesting are transitive.
|
|
|
|
while (isa<CXXRecordDecl>(DC)) {
|
|
|
|
CXXRecordDecl *Record = cast<CXXRecordDecl>(DC)->getCanonicalDecl();
|
|
|
|
Records.push_back(Record);
|
|
|
|
DC = Record->getDeclContext();
|
|
|
|
}
|
2010-02-10 17:31:12 +08:00
|
|
|
}
|
|
|
|
|
2010-03-24 13:22:00 +08:00
|
|
|
bool isDependent() const { return Dependent; }
|
|
|
|
|
2010-03-17 12:58:56 +08:00
|
|
|
bool includesClass(const CXXRecordDecl *R) const {
|
|
|
|
R = R->getCanonicalDecl();
|
|
|
|
return std::find(Records.begin(), Records.end(), R)
|
|
|
|
!= Records.end();
|
2010-02-10 17:31:12 +08:00
|
|
|
}
|
|
|
|
|
2010-03-24 15:46:06 +08:00
|
|
|
/// Retrieves the innermost "useful" context. Can be null if we're
|
|
|
|
/// doing access-control without privileges.
|
|
|
|
DeclContext *getInnerContext() const {
|
|
|
|
return Inner;
|
2010-03-24 13:22:00 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
typedef llvm::SmallVectorImpl<CXXRecordDecl*>::const_iterator record_iterator;
|
|
|
|
|
2010-03-24 15:46:06 +08:00
|
|
|
DeclContext *Inner;
|
2010-03-17 12:58:56 +08:00
|
|
|
llvm::SmallVector<CXXRecordDecl*, 4> Records;
|
2010-02-10 17:31:12 +08:00
|
|
|
FunctionDecl *Function;
|
2010-03-24 13:22:00 +08:00
|
|
|
bool Dependent;
|
2010-02-10 17:31:12 +08:00
|
|
|
};
|
|
|
|
}
|
2009-07-18 22:32:15 +08:00
|
|
|
|
2010-02-10 17:31:12 +08:00
|
|
|
static CXXRecordDecl *FindDeclaringClass(NamedDecl *D) {
|
|
|
|
CXXRecordDecl *DeclaringClass = cast<CXXRecordDecl>(D->getDeclContext());
|
|
|
|
while (DeclaringClass->isAnonymousStructOrUnion())
|
|
|
|
DeclaringClass = cast<CXXRecordDecl>(DeclaringClass->getDeclContext());
|
|
|
|
return DeclaringClass;
|
|
|
|
}
|
|
|
|
|
2010-03-24 13:22:00 +08:00
|
|
|
static bool MightInstantiateTo(Sema &S, DeclContext *Context,
|
|
|
|
DeclContext *Friend) {
|
|
|
|
if (Friend == Context)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
assert(!Friend->isDependentContext() &&
|
|
|
|
"can't handle friends with dependent contexts here");
|
|
|
|
|
|
|
|
if (!Context->isDependentContext())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (Friend->isFileContext())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// TODO: this is very conservative
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Asks whether the type in 'context' can ever instantiate to the type
|
|
|
|
// in 'friend'.
|
|
|
|
static bool MightInstantiateTo(Sema &S, CanQualType Context, CanQualType Friend) {
|
|
|
|
if (Friend == Context)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if (!Friend->isDependentType() && !Context->isDependentType())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// TODO: this is very conservative.
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool MightInstantiateTo(Sema &S,
|
|
|
|
FunctionDecl *Context,
|
|
|
|
FunctionDecl *Friend) {
|
|
|
|
if (Context->getDeclName() != Friend->getDeclName())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (!MightInstantiateTo(S,
|
|
|
|
Context->getDeclContext(),
|
|
|
|
Friend->getDeclContext()))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
CanQual<FunctionProtoType> FriendTy
|
|
|
|
= S.Context.getCanonicalType(Friend->getType())
|
|
|
|
->getAs<FunctionProtoType>();
|
|
|
|
CanQual<FunctionProtoType> ContextTy
|
|
|
|
= S.Context.getCanonicalType(Context->getType())
|
|
|
|
->getAs<FunctionProtoType>();
|
|
|
|
|
|
|
|
// There isn't any way that I know of to add qualifiers
|
|
|
|
// during instantiation.
|
|
|
|
if (FriendTy.getQualifiers() != ContextTy.getQualifiers())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (FriendTy->getNumArgs() != ContextTy->getNumArgs())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (!MightInstantiateTo(S,
|
|
|
|
ContextTy->getResultType(),
|
|
|
|
FriendTy->getResultType()))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
for (unsigned I = 0, E = FriendTy->getNumArgs(); I != E; ++I)
|
|
|
|
if (!MightInstantiateTo(S,
|
|
|
|
ContextTy->getArgType(I),
|
|
|
|
FriendTy->getArgType(I)))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool MightInstantiateTo(Sema &S,
|
|
|
|
FunctionTemplateDecl *Context,
|
|
|
|
FunctionTemplateDecl *Friend) {
|
|
|
|
return MightInstantiateTo(S,
|
|
|
|
Context->getTemplatedDecl(),
|
|
|
|
Friend->getTemplatedDecl());
|
|
|
|
}
|
|
|
|
|
2010-03-18 04:01:29 +08:00
|
|
|
static Sema::AccessResult MatchesFriend(Sema &S,
|
|
|
|
const EffectiveContext &EC,
|
|
|
|
const CXXRecordDecl *Friend) {
|
|
|
|
if (EC.includesClass(Friend))
|
|
|
|
return Sema::AR_accessible;
|
|
|
|
|
2010-03-24 13:22:00 +08:00
|
|
|
if (EC.isDependent()) {
|
|
|
|
CanQualType FriendTy
|
|
|
|
= S.Context.getCanonicalType(S.Context.getTypeDeclType(Friend));
|
|
|
|
|
|
|
|
for (EffectiveContext::record_iterator
|
|
|
|
I = EC.Records.begin(), E = EC.Records.end(); I != E; ++I) {
|
|
|
|
CanQualType ContextTy
|
|
|
|
= S.Context.getCanonicalType(S.Context.getTypeDeclType(*I));
|
|
|
|
if (MightInstantiateTo(S, ContextTy, FriendTy))
|
|
|
|
return Sema::AR_dependent;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-03-18 04:01:29 +08:00
|
|
|
return Sema::AR_inaccessible;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Sema::AccessResult MatchesFriend(Sema &S,
|
|
|
|
const EffectiveContext &EC,
|
2010-03-24 13:22:00 +08:00
|
|
|
CanQualType Friend) {
|
|
|
|
if (const RecordType *RT = Friend->getAs<RecordType>())
|
|
|
|
return MatchesFriend(S, EC, cast<CXXRecordDecl>(RT->getDecl()));
|
2010-03-18 04:01:29 +08:00
|
|
|
|
2010-03-24 13:22:00 +08:00
|
|
|
// TODO: we can do better than this
|
|
|
|
if (Friend->isDependentType())
|
|
|
|
return Sema::AR_dependent;
|
2010-03-18 04:01:29 +08:00
|
|
|
|
2010-03-24 13:22:00 +08:00
|
|
|
return Sema::AR_inaccessible;
|
|
|
|
}
|
2010-03-18 04:01:29 +08:00
|
|
|
|
2010-03-24 13:22:00 +08:00
|
|
|
/// Determines whether the given friend class template matches
|
|
|
|
/// anything in the effective context.
|
|
|
|
static Sema::AccessResult MatchesFriend(Sema &S,
|
|
|
|
const EffectiveContext &EC,
|
|
|
|
ClassTemplateDecl *Friend) {
|
|
|
|
Sema::AccessResult OnFailure = Sema::AR_inaccessible;
|
2010-03-18 04:01:29 +08:00
|
|
|
|
2010-03-24 13:22:00 +08:00
|
|
|
for (llvm::SmallVectorImpl<CXXRecordDecl*>::const_iterator
|
|
|
|
I = EC.Records.begin(), E = EC.Records.end(); I != E; ++I) {
|
|
|
|
CXXRecordDecl *Record = *I;
|
2010-03-18 04:01:29 +08:00
|
|
|
|
2010-03-24 13:22:00 +08:00
|
|
|
// Check whether the friend is the template of a class in the
|
|
|
|
// context chain. To do that, we need to figure out whether the
|
|
|
|
// current class has a template:
|
|
|
|
ClassTemplateDecl *CTD;
|
2010-03-18 04:01:29 +08:00
|
|
|
|
2010-03-24 13:22:00 +08:00
|
|
|
// A specialization of the template...
|
|
|
|
if (isa<ClassTemplateSpecializationDecl>(Record)) {
|
|
|
|
CTD = cast<ClassTemplateSpecializationDecl>(Record)
|
|
|
|
->getSpecializedTemplate();
|
2010-03-18 04:01:29 +08:00
|
|
|
|
2010-03-24 13:22:00 +08:00
|
|
|
// ... or the template pattern itself.
|
|
|
|
} else {
|
|
|
|
CTD = Record->getDescribedClassTemplate();
|
|
|
|
if (!CTD) continue;
|
2010-03-18 04:01:29 +08:00
|
|
|
}
|
|
|
|
|
2010-03-24 13:22:00 +08:00
|
|
|
// It's a match.
|
|
|
|
if (Friend == CTD->getCanonicalDecl())
|
|
|
|
return Sema::AR_accessible;
|
|
|
|
|
|
|
|
// If the template names don't match, it can't be a dependent
|
|
|
|
// match. This isn't true in C++0x because of template aliases.
|
|
|
|
if (!S.LangOpts.CPlusPlus0x && CTD->getDeclName() != Friend->getDeclName())
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// If the class's context can't instantiate to the friend's
|
|
|
|
// context, it can't be a dependent match.
|
|
|
|
if (!MightInstantiateTo(S, CTD->getDeclContext(),
|
|
|
|
Friend->getDeclContext()))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// Otherwise, it's a dependent match.
|
|
|
|
OnFailure = Sema::AR_dependent;
|
2010-03-18 04:01:29 +08:00
|
|
|
}
|
|
|
|
|
2010-03-24 13:22:00 +08:00
|
|
|
return OnFailure;
|
|
|
|
}
|
2010-03-18 04:01:29 +08:00
|
|
|
|
2010-03-24 13:22:00 +08:00
|
|
|
/// Determines whether the given friend function matches anything in
|
|
|
|
/// the effective context.
|
|
|
|
static Sema::AccessResult MatchesFriend(Sema &S,
|
|
|
|
const EffectiveContext &EC,
|
|
|
|
FunctionDecl *Friend) {
|
|
|
|
if (!EC.Function)
|
|
|
|
return Sema::AR_inaccessible;
|
2010-03-18 04:01:29 +08:00
|
|
|
|
2010-03-24 13:22:00 +08:00
|
|
|
if (Friend == EC.Function)
|
|
|
|
return Sema::AR_accessible;
|
|
|
|
|
|
|
|
if (EC.isDependent() && MightInstantiateTo(S, EC.Function, Friend))
|
|
|
|
return Sema::AR_dependent;
|
|
|
|
|
|
|
|
return Sema::AR_inaccessible;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Determines whether the given friend function template matches
|
|
|
|
/// anything in the effective context.
|
|
|
|
static Sema::AccessResult MatchesFriend(Sema &S,
|
|
|
|
const EffectiveContext &EC,
|
|
|
|
FunctionTemplateDecl *Friend) {
|
|
|
|
if (!EC.Function) return Sema::AR_inaccessible;
|
|
|
|
|
|
|
|
FunctionTemplateDecl *FTD = EC.Function->getPrimaryTemplate();
|
|
|
|
if (!FTD)
|
|
|
|
FTD = EC.Function->getDescribedFunctionTemplate();
|
|
|
|
if (!FTD)
|
2010-03-18 04:01:29 +08:00
|
|
|
return Sema::AR_inaccessible;
|
|
|
|
|
2010-03-24 13:22:00 +08:00
|
|
|
if (Friend == FTD->getCanonicalDecl())
|
2010-03-18 04:01:29 +08:00
|
|
|
return Sema::AR_accessible;
|
|
|
|
|
2010-03-24 13:22:00 +08:00
|
|
|
if (MightInstantiateTo(S, FTD, Friend))
|
|
|
|
return Sema::AR_dependent;
|
2010-03-18 04:01:29 +08:00
|
|
|
|
|
|
|
return Sema::AR_inaccessible;
|
|
|
|
}
|
|
|
|
|
2010-03-24 13:22:00 +08:00
|
|
|
/// Determines whether the given friend declaration matches anything
|
|
|
|
/// in the effective context.
|
|
|
|
static Sema::AccessResult MatchesFriend(Sema &S,
|
|
|
|
const EffectiveContext &EC,
|
|
|
|
FriendDecl *FriendD) {
|
|
|
|
if (Type *T = FriendD->getFriendType())
|
|
|
|
return MatchesFriend(S, EC, T->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));
|
|
|
|
}
|
|
|
|
|
2010-02-10 17:31:12 +08:00
|
|
|
static Sema::AccessResult GetFriendKind(Sema &S,
|
|
|
|
const EffectiveContext &EC,
|
|
|
|
const CXXRecordDecl *Class) {
|
2010-03-12 09:19:31 +08:00
|
|
|
// A class always has access to its own members.
|
2010-03-17 12:58:56 +08:00
|
|
|
if (EC.includesClass(Class))
|
2010-02-10 17:31:12 +08:00
|
|
|
return Sema::AR_accessible;
|
|
|
|
|
2010-03-17 12:58:56 +08:00
|
|
|
Sema::AccessResult OnFailure = Sema::AR_inaccessible;
|
|
|
|
|
2010-03-12 09:19:31 +08:00
|
|
|
// Okay, check friends.
|
|
|
|
for (CXXRecordDecl::friend_iterator I = Class->friend_begin(),
|
|
|
|
E = Class->friend_end(); I != E; ++I) {
|
|
|
|
FriendDecl *Friend = *I;
|
|
|
|
|
2010-03-18 04:01:29 +08:00
|
|
|
switch (MatchesFriend(S, EC, Friend)) {
|
|
|
|
case Sema::AR_accessible:
|
|
|
|
return Sema::AR_accessible;
|
2010-03-17 12:58:56 +08:00
|
|
|
|
2010-03-18 04:01:29 +08:00
|
|
|
case Sema::AR_inaccessible:
|
|
|
|
break;
|
2010-03-12 09:19:31 +08:00
|
|
|
|
2010-03-18 04:01:29 +08:00
|
|
|
case Sema::AR_dependent:
|
|
|
|
OnFailure = Sema::AR_dependent;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Sema::AR_delayed:
|
|
|
|
llvm_unreachable("cannot get delayed answer from MatchesFriend");
|
|
|
|
}
|
2010-03-12 09:19:31 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// That's it, give up.
|
2010-03-17 12:58:56 +08:00
|
|
|
return OnFailure;
|
2010-02-10 17:31:12 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Finds the best path from the naming class to the declaring class,
|
|
|
|
/// taking friend declarations into account.
|
|
|
|
///
|
2010-03-19 07:49:19 +08:00
|
|
|
/// \param FinalAccess the access of the "final step", or AS_none if
|
|
|
|
/// there is no final step.
|
2010-02-10 17:31:12 +08:00
|
|
|
/// \return null if friendship is dependent
|
|
|
|
static CXXBasePath *FindBestPath(Sema &S,
|
|
|
|
const EffectiveContext &EC,
|
|
|
|
CXXRecordDecl *Derived,
|
|
|
|
CXXRecordDecl *Base,
|
2010-03-19 07:49:19 +08:00
|
|
|
AccessSpecifier FinalAccess,
|
2010-02-10 17:31:12 +08:00
|
|
|
CXXBasePaths &Paths) {
|
|
|
|
// Derive the paths to the desired base.
|
|
|
|
bool isDerived = Derived->isDerivedFrom(Base, Paths);
|
|
|
|
assert(isDerived && "derived class not actually derived from base");
|
|
|
|
(void) isDerived;
|
|
|
|
|
|
|
|
CXXBasePath *BestPath = 0;
|
|
|
|
|
2010-03-19 07:49:19 +08:00
|
|
|
assert(FinalAccess != AS_none && "forbidden access after declaring class");
|
|
|
|
|
2010-03-24 13:22:00 +08:00
|
|
|
bool AnyDependent = false;
|
|
|
|
|
2010-02-10 17:31:12 +08:00
|
|
|
// Derive the friend-modified access along each path.
|
|
|
|
for (CXXBasePaths::paths_iterator PI = Paths.begin(), PE = Paths.end();
|
|
|
|
PI != PE; ++PI) {
|
|
|
|
|
|
|
|
// Walk through the path backwards.
|
2010-03-19 07:49:19 +08:00
|
|
|
AccessSpecifier PathAccess = FinalAccess;
|
2010-02-10 17:31:12 +08:00
|
|
|
CXXBasePath::iterator I = PI->end(), E = PI->begin();
|
|
|
|
while (I != E) {
|
|
|
|
--I;
|
|
|
|
|
2010-03-19 07:49:19 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2010-02-10 17:31:12 +08:00
|
|
|
AccessSpecifier BaseAccess = I->Base->getAccessSpecifier();
|
|
|
|
if (BaseAccess != AS_public) {
|
|
|
|
switch (GetFriendKind(S, EC, I->Class)) {
|
2010-03-19 07:49:19 +08:00
|
|
|
case Sema::AR_inaccessible:
|
|
|
|
PathAccess = CXXRecordDecl::MergeAccess(BaseAccess, PathAccess);
|
|
|
|
break;
|
|
|
|
case Sema::AR_accessible:
|
|
|
|
PathAccess = AS_public;
|
|
|
|
break;
|
|
|
|
case Sema::AR_dependent:
|
2010-03-24 13:22:00 +08:00
|
|
|
AnyDependent = true;
|
|
|
|
goto Next;
|
2010-02-10 17:31:12 +08:00
|
|
|
case Sema::AR_delayed:
|
|
|
|
llvm_unreachable("friend resolution is never delayed"); break;
|
|
|
|
}
|
2009-03-27 14:03:27 +08:00
|
|
|
}
|
|
|
|
}
|
2009-07-18 22:32:15 +08:00
|
|
|
|
2010-02-10 17:31:12 +08:00
|
|
|
// Note that we modify the path's Access field to the
|
|
|
|
// friend-modified access.
|
|
|
|
if (BestPath == 0 || PathAccess < BestPath->Access) {
|
|
|
|
BestPath = &*PI;
|
|
|
|
BestPath->Access = PathAccess;
|
2010-03-24 13:22:00 +08:00
|
|
|
|
|
|
|
// Short-circuit if we found a public path.
|
|
|
|
if (BestPath->Access == AS_public)
|
|
|
|
return BestPath;
|
2009-03-27 14:03:27 +08:00
|
|
|
}
|
2010-03-24 13:22:00 +08:00
|
|
|
|
|
|
|
Next: ;
|
2009-03-27 14:03:27 +08:00
|
|
|
}
|
|
|
|
|
2010-03-24 13:22:00 +08:00
|
|
|
assert((!BestPath || BestPath->Access != AS_public) &&
|
|
|
|
"fell out of loop with public path");
|
|
|
|
|
|
|
|
// We didn't find a public path, but at least one path was subject
|
|
|
|
// to dependent friendship, so delay the check.
|
|
|
|
if (AnyDependent)
|
|
|
|
return 0;
|
|
|
|
|
2010-02-10 17:31:12 +08:00
|
|
|
return BestPath;
|
2009-07-18 22:32:15 +08:00
|
|
|
}
|
|
|
|
|
2010-02-10 17:31:12 +08:00
|
|
|
/// Diagnose the path which caused the given declaration or base class
|
|
|
|
/// to become inaccessible.
|
|
|
|
static void DiagnoseAccessPath(Sema &S,
|
|
|
|
const EffectiveContext &EC,
|
|
|
|
CXXRecordDecl *NamingClass,
|
|
|
|
CXXRecordDecl *DeclaringClass,
|
|
|
|
NamedDecl *D, AccessSpecifier Access) {
|
|
|
|
// 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.
|
|
|
|
//
|
|
|
|
// DependentFriend should be impossible here.
|
|
|
|
if (D && (Access == D->getAccess() || D->getAccess() == AS_private)) {
|
|
|
|
switch (GetFriendKind(S, EC, DeclaringClass)) {
|
|
|
|
case Sema::AR_inaccessible: {
|
|
|
|
S.Diag(D->getLocation(), diag::note_access_natural)
|
|
|
|
<< (unsigned) (Access == AS_protected)
|
|
|
|
<< /*FIXME: not implicitly*/ 0;
|
|
|
|
return;
|
|
|
|
}
|
2009-07-18 22:32:15 +08:00
|
|
|
|
2010-02-10 17:31:12 +08:00
|
|
|
case Sema::AR_accessible: break;
|
2009-07-18 22:32:15 +08:00
|
|
|
|
2010-02-10 17:31:12 +08:00
|
|
|
case Sema::AR_dependent:
|
|
|
|
case Sema::AR_delayed:
|
|
|
|
llvm_unreachable("dependent/delayed not allowed");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2009-03-27 14:03:27 +08:00
|
|
|
|
2010-02-10 17:31:12 +08:00
|
|
|
CXXBasePaths Paths;
|
2010-03-19 07:49:19 +08:00
|
|
|
CXXBasePath &Path = *FindBestPath(S, EC, NamingClass, DeclaringClass,
|
|
|
|
AS_public, Paths);
|
2009-07-18 22:32:15 +08:00
|
|
|
|
2010-02-10 17:31:12 +08:00
|
|
|
CXXBasePath::iterator I = Path.end(), E = Path.begin();
|
|
|
|
while (I != E) {
|
|
|
|
--I;
|
2009-03-27 14:03:27 +08:00
|
|
|
|
2010-02-10 17:31:12 +08:00
|
|
|
const CXXBaseSpecifier *BS = I->Base;
|
|
|
|
AccessSpecifier BaseAccess = BS->getAccessSpecifier();
|
|
|
|
|
|
|
|
// If this is public inheritance, or the derived class is a friend,
|
|
|
|
// skip this step.
|
|
|
|
if (BaseAccess == AS_public)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
switch (GetFriendKind(S, EC, I->Class)) {
|
|
|
|
case 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");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check whether this base specifier is the tighest point
|
|
|
|
// constraining access. We have to check against AS_private for
|
|
|
|
// the same reasons as above.
|
|
|
|
if (BaseAccess == AS_private || BaseAccess >= Access) {
|
|
|
|
|
|
|
|
// We're constrained by inheritance, but we want to say
|
|
|
|
// "declared private here" if we're diagnosing a hierarchy
|
|
|
|
// conversion and this is the final step.
|
|
|
|
unsigned diagnostic;
|
|
|
|
if (D) diagnostic = diag::note_access_constrained_by_path;
|
|
|
|
else if (I + 1 == Path.end()) diagnostic = diag::note_access_natural;
|
|
|
|
else diagnostic = diag::note_access_constrained_by_path;
|
|
|
|
|
|
|
|
S.Diag(BS->getSourceRange().getBegin(), diagnostic)
|
|
|
|
<< BS->getSourceRange()
|
|
|
|
<< (BaseAccess == AS_protected)
|
|
|
|
<< (BS->getAccessSpecifierAsWritten() == AS_none);
|
|
|
|
return;
|
|
|
|
}
|
2009-03-27 14:03:27 +08:00
|
|
|
}
|
2009-07-18 22:32:15 +08:00
|
|
|
|
2010-02-10 17:31:12 +08:00
|
|
|
llvm_unreachable("access not apparently constrained by path");
|
2009-03-27 13:05:05 +08:00
|
|
|
}
|
2010-01-23 08:46:32 +08:00
|
|
|
|
2010-02-10 17:31:12 +08:00
|
|
|
/// Diagnose an inaccessible class member.
|
|
|
|
static void DiagnoseInaccessibleMember(Sema &S, SourceLocation Loc,
|
|
|
|
const EffectiveContext &EC,
|
|
|
|
CXXRecordDecl *NamingClass,
|
|
|
|
AccessSpecifier Access,
|
|
|
|
const Sema::AccessedEntity &Entity) {
|
|
|
|
NamedDecl *D = Entity.getTargetDecl();
|
|
|
|
CXXRecordDecl *DeclaringClass = FindDeclaringClass(D);
|
|
|
|
|
2010-03-16 13:22:47 +08:00
|
|
|
S.Diag(Loc, Entity.getDiag())
|
|
|
|
<< (Access == AS_protected)
|
|
|
|
<< D->getDeclName()
|
|
|
|
<< S.Context.getTypeDeclType(NamingClass)
|
|
|
|
<< S.Context.getTypeDeclType(DeclaringClass);
|
2010-02-10 17:31:12 +08:00
|
|
|
DiagnoseAccessPath(S, EC, NamingClass, DeclaringClass, D, Access);
|
|
|
|
}
|
2010-01-23 08:46:32 +08:00
|
|
|
|
2010-02-10 17:31:12 +08:00
|
|
|
/// Diagnose an inaccessible hierarchy conversion.
|
|
|
|
static void DiagnoseInaccessibleBase(Sema &S, SourceLocation Loc,
|
|
|
|
const EffectiveContext &EC,
|
|
|
|
AccessSpecifier Access,
|
2010-03-16 13:22:47 +08:00
|
|
|
const Sema::AccessedEntity &Entity) {
|
|
|
|
S.Diag(Loc, Entity.getDiag())
|
|
|
|
<< (Access == AS_protected)
|
|
|
|
<< DeclarationName()
|
|
|
|
<< S.Context.getTypeDeclType(Entity.getDerivedClass())
|
|
|
|
<< S.Context.getTypeDeclType(Entity.getBaseClass());
|
2010-02-10 17:31:12 +08:00
|
|
|
DiagnoseAccessPath(S, EC, Entity.getDerivedClass(),
|
|
|
|
Entity.getBaseClass(), 0, Access);
|
2010-01-23 08:46:32 +08:00
|
|
|
}
|
|
|
|
|
2010-03-16 13:22:47 +08:00
|
|
|
static void DiagnoseBadAccess(Sema &S, SourceLocation Loc,
|
2010-02-10 17:31:12 +08:00
|
|
|
const EffectiveContext &EC,
|
|
|
|
CXXRecordDecl *NamingClass,
|
|
|
|
AccessSpecifier Access,
|
2010-03-16 13:22:47 +08:00
|
|
|
const Sema::AccessedEntity &Entity) {
|
2010-02-10 17:31:12 +08:00
|
|
|
if (Entity.isMemberAccess())
|
|
|
|
DiagnoseInaccessibleMember(S, Loc, EC, NamingClass, Access, Entity);
|
|
|
|
else
|
2010-03-16 13:22:47 +08:00
|
|
|
DiagnoseInaccessibleBase(S, Loc, EC, Access, Entity);
|
2010-02-10 17:31:12 +08:00
|
|
|
}
|
2010-01-23 08:46:32 +08:00
|
|
|
|
|
|
|
|
2010-02-10 17:31:12 +08:00
|
|
|
/// Try to elevate access using friend declarations. This is
|
|
|
|
/// potentially quite expensive.
|
2010-03-24 13:22:00 +08:00
|
|
|
///
|
|
|
|
/// \return true if elevation was dependent
|
|
|
|
static bool TryElevateAccess(Sema &S,
|
2010-02-10 17:31:12 +08:00
|
|
|
const EffectiveContext &EC,
|
|
|
|
const Sema::AccessedEntity &Entity,
|
|
|
|
AccessSpecifier &Access) {
|
|
|
|
CXXRecordDecl *DeclaringClass;
|
|
|
|
if (Entity.isMemberAccess()) {
|
|
|
|
DeclaringClass = FindDeclaringClass(Entity.getTargetDecl());
|
|
|
|
} else {
|
|
|
|
DeclaringClass = Entity.getBaseClass();
|
2010-01-27 11:50:35 +08:00
|
|
|
}
|
2010-02-10 17:31:12 +08:00
|
|
|
CXXRecordDecl *NamingClass = Entity.getNamingClass();
|
|
|
|
|
|
|
|
// Adjust the declaration of the referred entity.
|
2010-03-19 07:49:19 +08:00
|
|
|
AccessSpecifier DeclAccess = AS_public;
|
2010-02-10 17:31:12 +08:00
|
|
|
if (Entity.isMemberAccess()) {
|
|
|
|
NamedDecl *Target = Entity.getTargetDecl();
|
|
|
|
|
|
|
|
DeclAccess = Target->getAccess();
|
|
|
|
if (DeclAccess != AS_public) {
|
|
|
|
switch (GetFriendKind(S, EC, DeclaringClass)) {
|
|
|
|
case Sema::AR_accessible: DeclAccess = AS_public; break;
|
|
|
|
case Sema::AR_inaccessible: break;
|
2010-03-24 13:22:00 +08:00
|
|
|
case Sema::AR_dependent: return true;
|
2010-02-10 17:31:12 +08:00
|
|
|
case Sema::AR_delayed: llvm_unreachable("friend status is never delayed");
|
|
|
|
}
|
|
|
|
}
|
2010-01-27 11:50:35 +08:00
|
|
|
|
2010-02-10 17:31:12 +08:00
|
|
|
if (DeclaringClass == NamingClass) {
|
|
|
|
Access = DeclAccess;
|
2010-03-24 13:22:00 +08:00
|
|
|
return false;
|
2010-02-10 17:31:12 +08:00
|
|
|
}
|
|
|
|
}
|
2010-01-27 11:50:35 +08:00
|
|
|
|
2010-02-10 17:31:12 +08:00
|
|
|
assert(DeclaringClass != NamingClass);
|
|
|
|
|
|
|
|
// Append the declaration's access if applicable.
|
|
|
|
CXXBasePaths Paths;
|
|
|
|
CXXBasePath *Path = FindBestPath(S, EC, Entity.getNamingClass(),
|
2010-03-19 07:49:19 +08:00
|
|
|
DeclaringClass, DeclAccess, Paths);
|
2010-03-24 13:22:00 +08:00
|
|
|
if (!Path)
|
|
|
|
return true;
|
2010-01-23 08:46:32 +08:00
|
|
|
|
2010-03-19 07:49:19 +08:00
|
|
|
// Grab the access along the best path (note that this includes the
|
|
|
|
// final-step access).
|
2010-02-10 17:31:12 +08:00
|
|
|
AccessSpecifier NewAccess = Path->Access;
|
|
|
|
assert(NewAccess <= Access && "access along best path worse than direct?");
|
|
|
|
Access = NewAccess;
|
2010-03-24 13:22:00 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void DelayAccess(Sema &S,
|
|
|
|
const EffectiveContext &EC,
|
|
|
|
SourceLocation Loc,
|
|
|
|
const Sema::AccessedEntity &Entity) {
|
|
|
|
assert(EC.isDependent() && "delaying non-dependent access");
|
2010-03-24 15:46:06 +08:00
|
|
|
DeclContext *DC = EC.getInnerContext();
|
2010-03-24 13:22:00 +08:00
|
|
|
assert(DC->isDependentContext() && "delaying non-dependent access");
|
|
|
|
DependentDiagnostic::Create(S.Context, DC, DependentDiagnostic::Access,
|
|
|
|
Loc,
|
|
|
|
Entity.isMemberAccess(),
|
|
|
|
Entity.getAccess(),
|
|
|
|
Entity.getTargetDecl(),
|
|
|
|
Entity.getNamingClass(),
|
|
|
|
Entity.getDiag());
|
2010-02-10 17:31:12 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Checks access to an entity from the given effective context.
|
|
|
|
static Sema::AccessResult CheckEffectiveAccess(Sema &S,
|
|
|
|
const EffectiveContext &EC,
|
|
|
|
SourceLocation Loc,
|
2010-03-16 13:22:47 +08:00
|
|
|
Sema::AccessedEntity const &Entity) {
|
2010-02-10 17:31:12 +08:00
|
|
|
AccessSpecifier Access = Entity.getAccess();
|
2010-03-17 12:58:56 +08:00
|
|
|
assert(Access != AS_public && "called for public access!");
|
2010-02-10 17:31:12 +08:00
|
|
|
|
2010-03-17 12:58:56 +08:00
|
|
|
// Find a non-anonymous naming class. For records with access,
|
|
|
|
// there should always be one of these.
|
2010-02-10 17:31:12 +08:00
|
|
|
CXXRecordDecl *NamingClass = Entity.getNamingClass();
|
2010-01-23 08:46:32 +08:00
|
|
|
while (NamingClass->isAnonymousStructOrUnion())
|
2010-03-16 13:22:47 +08:00
|
|
|
NamingClass = cast<CXXRecordDecl>(NamingClass->getParent());
|
2010-01-23 08:46:32 +08:00
|
|
|
|
2010-03-17 12:58:56 +08:00
|
|
|
// White-list accesses from classes with privileges equivalent to the
|
|
|
|
// naming class --- but only if the access path isn't forbidden
|
|
|
|
// (i.e. an access of a private member from a subclass).
|
|
|
|
if (Access != AS_none && EC.includesClass(NamingClass))
|
2010-02-10 17:31:12 +08:00
|
|
|
return Sema::AR_accessible;
|
2010-03-17 12:58:56 +08:00
|
|
|
|
|
|
|
// Try to elevate access.
|
|
|
|
// TODO: on some code, it might be better to do the protected check
|
|
|
|
// without trying to elevate first.
|
2010-03-24 13:22:00 +08:00
|
|
|
if (TryElevateAccess(S, EC, Entity, Access)) {
|
|
|
|
DelayAccess(S, EC, Loc, Entity);
|
|
|
|
return Sema::AR_dependent;
|
|
|
|
}
|
|
|
|
|
2010-03-17 12:58:56 +08:00
|
|
|
if (Access == AS_public) return Sema::AR_accessible;
|
2010-01-23 08:46:32 +08:00
|
|
|
|
|
|
|
// Protected access.
|
|
|
|
if (Access == AS_protected) {
|
|
|
|
// FIXME: implement [class.protected]p1
|
2010-03-17 12:58:56 +08:00
|
|
|
for (llvm::SmallVectorImpl<CXXRecordDecl*>::const_iterator
|
|
|
|
I = EC.Records.begin(), E = EC.Records.end(); I != E; ++I)
|
|
|
|
if ((*I)->isDerivedFrom(NamingClass))
|
|
|
|
return Sema::AR_accessible;
|
2010-01-23 08:46:32 +08:00
|
|
|
|
2010-03-17 12:58:56 +08:00
|
|
|
// FIXME: delay if we can't decide class derivation yet.
|
2010-01-23 08:46:32 +08:00
|
|
|
}
|
|
|
|
|
2010-02-10 17:31:12 +08:00
|
|
|
// Okay, that's it, reject it.
|
2010-03-16 13:22:47 +08:00
|
|
|
if (!Entity.isQuiet())
|
|
|
|
DiagnoseBadAccess(S, Loc, EC, NamingClass, Access, Entity);
|
2010-02-10 17:31:12 +08:00
|
|
|
return Sema::AR_inaccessible;
|
|
|
|
}
|
2010-01-23 08:46:32 +08:00
|
|
|
|
2010-02-10 17:31:12 +08:00
|
|
|
static Sema::AccessResult CheckAccess(Sema &S, SourceLocation Loc,
|
2010-03-16 13:22:47 +08:00
|
|
|
const Sema::AccessedEntity &Entity) {
|
2010-02-10 17:31:12 +08:00
|
|
|
// If the access path is public, it's accessible everywhere.
|
|
|
|
if (Entity.getAccess() == AS_public)
|
|
|
|
return Sema::AR_accessible;
|
2010-01-23 08:46:32 +08:00
|
|
|
|
2010-02-10 17:31:12 +08:00
|
|
|
// If we're currently parsing a top-level declaration, delay
|
|
|
|
// diagnostics. This is the only case where parsing a declaration
|
|
|
|
// can actually change our effective context for the purposes of
|
|
|
|
// access control.
|
|
|
|
if (S.CurContext->isFileContext() && S.ParsingDeclDepth) {
|
|
|
|
S.DelayedDiagnostics.push_back(
|
|
|
|
Sema::DelayedDiagnostic::makeAccess(Loc, Entity));
|
|
|
|
return Sema::AR_delayed;
|
2010-01-23 08:46:32 +08:00
|
|
|
}
|
|
|
|
|
2010-02-10 17:31:12 +08:00
|
|
|
return CheckEffectiveAccess(S, EffectiveContext(S.CurContext),
|
2010-03-16 13:22:47 +08:00
|
|
|
Loc, Entity);
|
2010-01-23 08:46:32 +08:00
|
|
|
}
|
|
|
|
|
2010-01-27 11:50:35 +08:00
|
|
|
void Sema::HandleDelayedAccessCheck(DelayedDiagnostic &DD, Decl *Ctx) {
|
|
|
|
// Pretend we did this from the context of the newly-parsed
|
|
|
|
// declaration.
|
2010-02-10 17:31:12 +08:00
|
|
|
EffectiveContext EC(Ctx->getDeclContext());
|
2010-01-27 11:50:35 +08:00
|
|
|
|
2010-03-16 13:22:47 +08:00
|
|
|
if (CheckEffectiveAccess(*this, EC, DD.Loc, DD.getAccessData()))
|
2010-01-27 11:50:35 +08:00
|
|
|
DD.Triggered = true;
|
|
|
|
}
|
|
|
|
|
2010-03-24 13:22:00 +08:00
|
|
|
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()) {
|
|
|
|
AccessedEntity Entity(AccessedEntity::Member,
|
|
|
|
cast<CXXRecordDecl>(NamingD),
|
|
|
|
Access,
|
|
|
|
cast<NamedDecl>(TargetD));
|
|
|
|
Entity.setDiag(DD.getDiagnostic());
|
|
|
|
CheckAccess(*this, Loc, Entity);
|
|
|
|
} else {
|
|
|
|
AccessedEntity Entity(AccessedEntity::Base,
|
|
|
|
cast<CXXRecordDecl>(TargetD),
|
|
|
|
cast<CXXRecordDecl>(NamingD),
|
|
|
|
Access);
|
|
|
|
Entity.setDiag(DD.getDiagnostic());
|
|
|
|
CheckAccess(*this, Loc, Entity);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-02-10 17:31:12 +08:00
|
|
|
Sema::AccessResult Sema::CheckUnresolvedLookupAccess(UnresolvedLookupExpr *E,
|
2010-03-19 15:35:19 +08:00
|
|
|
DeclAccessPair Found) {
|
2010-03-16 13:22:47 +08:00
|
|
|
if (!getLangOptions().AccessControl ||
|
|
|
|
!E->getNamingClass() ||
|
2010-03-19 15:35:19 +08:00
|
|
|
Found.getAccess() == AS_public)
|
2010-02-10 17:31:12 +08:00
|
|
|
return AR_accessible;
|
2010-01-27 09:50:18 +08:00
|
|
|
|
2010-03-19 15:35:19 +08:00
|
|
|
AccessedEntity Entity(AccessedEntity::Member, E->getNamingClass(), Found);
|
2010-03-16 13:22:47 +08:00
|
|
|
Entity.setDiag(diag::err_access) << E->getSourceRange();
|
|
|
|
|
|
|
|
return CheckAccess(*this, E->getNameLoc(), Entity);
|
2010-01-27 09:50:18 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Perform access-control checking on a previously-unresolved member
|
|
|
|
/// access which has now been resolved to a member.
|
2010-02-10 17:31:12 +08:00
|
|
|
Sema::AccessResult Sema::CheckUnresolvedMemberAccess(UnresolvedMemberExpr *E,
|
2010-03-19 15:35:19 +08:00
|
|
|
DeclAccessPair Found) {
|
2010-03-16 13:22:47 +08:00
|
|
|
if (!getLangOptions().AccessControl ||
|
2010-03-19 15:35:19 +08:00
|
|
|
Found.getAccess() == AS_public)
|
2010-02-10 17:31:12 +08:00
|
|
|
return AR_accessible;
|
2010-01-27 09:50:18 +08:00
|
|
|
|
2010-03-19 15:35:19 +08:00
|
|
|
AccessedEntity Entity(AccessedEntity::Member, E->getNamingClass(), Found);
|
2010-03-16 13:22:47 +08:00
|
|
|
Entity.setDiag(diag::err_access) << E->getSourceRange();
|
|
|
|
|
|
|
|
return CheckAccess(*this, E->getMemberLoc(), Entity);
|
2010-01-27 09:50:18 +08:00
|
|
|
}
|
|
|
|
|
2010-02-10 17:31:12 +08:00
|
|
|
Sema::AccessResult Sema::CheckDestructorAccess(SourceLocation Loc,
|
2010-03-16 13:22:47 +08:00
|
|
|
CXXDestructorDecl *Dtor,
|
|
|
|
const PartialDiagnostic &PDiag) {
|
2010-02-02 16:45:54 +08:00
|
|
|
if (!getLangOptions().AccessControl)
|
2010-02-10 17:31:12 +08:00
|
|
|
return AR_accessible;
|
2010-02-02 16:45:54 +08:00
|
|
|
|
2010-03-16 13:22:47 +08:00
|
|
|
// There's never a path involved when checking implicit destructor access.
|
2010-02-02 16:45:54 +08:00
|
|
|
AccessSpecifier Access = Dtor->getAccess();
|
|
|
|
if (Access == AS_public)
|
2010-02-10 17:31:12 +08:00
|
|
|
return AR_accessible;
|
2010-02-02 16:45:54 +08:00
|
|
|
|
2010-03-16 13:22:47 +08:00
|
|
|
CXXRecordDecl *NamingClass = Dtor->getParent();
|
2010-03-19 15:35:19 +08:00
|
|
|
AccessedEntity Entity(AccessedEntity::Member, NamingClass,
|
|
|
|
DeclAccessPair::make(Dtor, Access));
|
2010-03-16 13:22:47 +08:00
|
|
|
Entity.setDiag(PDiag); // TODO: avoid copy
|
|
|
|
|
|
|
|
return CheckAccess(*this, Loc, Entity);
|
2010-02-02 16:45:54 +08:00
|
|
|
}
|
|
|
|
|
2010-02-01 11:16:54 +08:00
|
|
|
/// Checks access to a constructor.
|
2010-02-10 17:31:12 +08:00
|
|
|
Sema::AccessResult Sema::CheckConstructorAccess(SourceLocation UseLoc,
|
2010-02-01 11:16:54 +08:00
|
|
|
CXXConstructorDecl *Constructor,
|
|
|
|
AccessSpecifier Access) {
|
2010-03-16 13:22:47 +08:00
|
|
|
if (!getLangOptions().AccessControl ||
|
|
|
|
Access == AS_public)
|
2010-02-10 17:31:12 +08:00
|
|
|
return AR_accessible;
|
2010-02-01 11:16:54 +08:00
|
|
|
|
2010-02-10 17:31:12 +08:00
|
|
|
CXXRecordDecl *NamingClass = Constructor->getParent();
|
2010-03-19 15:35:19 +08:00
|
|
|
AccessedEntity Entity(AccessedEntity::Member, NamingClass,
|
|
|
|
DeclAccessPair::make(Constructor, Access));
|
2010-03-16 13:22:47 +08:00
|
|
|
Entity.setDiag(diag::err_access_ctor);
|
|
|
|
|
|
|
|
return CheckAccess(*this, UseLoc, Entity);
|
2010-02-01 11:16:54 +08:00
|
|
|
}
|
|
|
|
|
2010-03-16 14:11:48 +08:00
|
|
|
/// Checks direct (i.e. non-inherited) access to an arbitrary class
|
|
|
|
/// member.
|
|
|
|
Sema::AccessResult Sema::CheckDirectMemberAccess(SourceLocation UseLoc,
|
|
|
|
NamedDecl *Target,
|
|
|
|
const PartialDiagnostic &Diag) {
|
|
|
|
AccessSpecifier Access = Target->getAccess();
|
|
|
|
if (!getLangOptions().AccessControl ||
|
|
|
|
Access == AS_public)
|
|
|
|
return AR_accessible;
|
|
|
|
|
|
|
|
CXXRecordDecl *NamingClass = cast<CXXRecordDecl>(Target->getDeclContext());
|
2010-03-19 15:35:19 +08:00
|
|
|
AccessedEntity Entity(AccessedEntity::Member, NamingClass,
|
|
|
|
DeclAccessPair::make(Target, Access));
|
2010-03-16 14:11:48 +08:00
|
|
|
Entity.setDiag(Diag);
|
|
|
|
return CheckAccess(*this, UseLoc, Entity);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-03-18 16:19:33 +08:00
|
|
|
/// Checks access to an overloaded operator new or delete.
|
|
|
|
Sema::AccessResult Sema::CheckAllocationAccess(SourceLocation OpLoc,
|
|
|
|
SourceRange PlacementRange,
|
|
|
|
CXXRecordDecl *NamingClass,
|
2010-03-19 15:35:19 +08:00
|
|
|
DeclAccessPair Found) {
|
2010-03-18 16:19:33 +08:00
|
|
|
if (!getLangOptions().AccessControl ||
|
|
|
|
!NamingClass ||
|
2010-03-19 15:35:19 +08:00
|
|
|
Found.getAccess() == AS_public)
|
2010-03-18 16:19:33 +08:00
|
|
|
return AR_accessible;
|
|
|
|
|
2010-03-19 15:35:19 +08:00
|
|
|
AccessedEntity Entity(AccessedEntity::Member, NamingClass, Found);
|
2010-03-18 16:19:33 +08:00
|
|
|
Entity.setDiag(diag::err_access)
|
|
|
|
<< PlacementRange;
|
|
|
|
|
|
|
|
return CheckAccess(*this, OpLoc, Entity);
|
|
|
|
}
|
|
|
|
|
2010-02-01 11:16:54 +08:00
|
|
|
/// Checks access to an overloaded member operator, including
|
|
|
|
/// conversion operators.
|
2010-02-10 17:31:12 +08:00
|
|
|
Sema::AccessResult Sema::CheckMemberOperatorAccess(SourceLocation OpLoc,
|
|
|
|
Expr *ObjectExpr,
|
2010-03-16 13:22:47 +08:00
|
|
|
Expr *ArgExpr,
|
2010-03-19 15:35:19 +08:00
|
|
|
DeclAccessPair Found) {
|
2010-03-16 13:22:47 +08:00
|
|
|
if (!getLangOptions().AccessControl ||
|
2010-03-19 15:35:19 +08:00
|
|
|
Found.getAccess() == AS_public)
|
2010-02-10 17:31:12 +08:00
|
|
|
return AR_accessible;
|
2010-01-28 09:42:12 +08:00
|
|
|
|
|
|
|
const RecordType *RT = ObjectExpr->getType()->getAs<RecordType>();
|
|
|
|
assert(RT && "found member operator but object expr not of record type");
|
|
|
|
CXXRecordDecl *NamingClass = cast<CXXRecordDecl>(RT->getDecl());
|
|
|
|
|
2010-03-19 15:35:19 +08:00
|
|
|
AccessedEntity Entity(AccessedEntity::Member, NamingClass, Found);
|
2010-03-16 13:22:47 +08:00
|
|
|
Entity.setDiag(diag::err_access)
|
|
|
|
<< ObjectExpr->getSourceRange()
|
|
|
|
<< (ArgExpr ? ArgExpr->getSourceRange() : SourceRange());
|
|
|
|
|
|
|
|
return CheckAccess(*this, OpLoc, Entity);
|
2010-02-10 17:31:12 +08:00
|
|
|
}
|
2010-01-28 09:42:12 +08:00
|
|
|
|
2010-02-10 17:31:12 +08:00
|
|
|
/// Checks access for a hierarchy conversion.
|
|
|
|
///
|
|
|
|
/// \param IsBaseToDerived whether this is a base-to-derived conversion (true)
|
|
|
|
/// or a derived-to-base conversion (false)
|
|
|
|
/// \param ForceCheck true if this check should be performed even if access
|
|
|
|
/// control is disabled; some things rely on this for semantics
|
|
|
|
/// \param ForceUnprivileged true if this check should proceed as if the
|
|
|
|
/// context had no special privileges
|
|
|
|
/// \param ADK controls the kind of diagnostics that are used
|
|
|
|
Sema::AccessResult Sema::CheckBaseClassAccess(SourceLocation AccessLoc,
|
|
|
|
QualType Base,
|
|
|
|
QualType Derived,
|
|
|
|
const CXXBasePath &Path,
|
2010-03-16 13:22:47 +08:00
|
|
|
unsigned DiagID,
|
2010-02-10 17:31:12 +08:00
|
|
|
bool ForceCheck,
|
2010-03-16 13:22:47 +08:00
|
|
|
bool ForceUnprivileged) {
|
2010-02-10 17:31:12 +08:00
|
|
|
if (!ForceCheck && !getLangOptions().AccessControl)
|
|
|
|
return AR_accessible;
|
|
|
|
|
|
|
|
if (Path.Access == AS_public)
|
|
|
|
return AR_accessible;
|
|
|
|
|
|
|
|
CXXRecordDecl *BaseD, *DerivedD;
|
|
|
|
BaseD = cast<CXXRecordDecl>(Base->getAs<RecordType>()->getDecl());
|
|
|
|
DerivedD = cast<CXXRecordDecl>(Derived->getAs<RecordType>()->getDecl());
|
2010-03-16 13:22:47 +08:00
|
|
|
|
|
|
|
AccessedEntity Entity(AccessedEntity::Base, BaseD, DerivedD, Path.Access);
|
|
|
|
if (DiagID)
|
|
|
|
Entity.setDiag(DiagID) << Derived << Base;
|
2010-02-10 17:31:12 +08:00
|
|
|
|
|
|
|
if (ForceUnprivileged)
|
2010-03-16 13:22:47 +08:00
|
|
|
return CheckEffectiveAccess(*this, EffectiveContext(), AccessLoc, Entity);
|
|
|
|
return CheckAccess(*this, AccessLoc, Entity);
|
2010-01-28 09:42:12 +08:00
|
|
|
}
|
|
|
|
|
2010-01-23 08:46:32 +08:00
|
|
|
/// Checks access to all the declarations in the given result set.
|
2010-02-10 17:31:12 +08:00
|
|
|
void Sema::CheckLookupAccess(const LookupResult &R) {
|
|
|
|
assert(getLangOptions().AccessControl
|
|
|
|
&& "performing access check without access control");
|
|
|
|
assert(R.getNamingClass() && "performing access check without naming class");
|
|
|
|
|
2010-03-16 13:22:47 +08:00
|
|
|
for (LookupResult::iterator I = R.begin(), E = R.end(); I != E; ++I) {
|
|
|
|
if (I.getAccess() != AS_public) {
|
|
|
|
AccessedEntity Entity(AccessedEntity::Member,
|
2010-03-19 15:35:19 +08:00
|
|
|
R.getNamingClass(),
|
|
|
|
I.getPair());
|
2010-03-16 13:22:47 +08:00
|
|
|
Entity.setDiag(diag::err_access);
|
|
|
|
|
|
|
|
CheckAccess(*this, R.getNameLoc(), Entity);
|
|
|
|
}
|
|
|
|
}
|
2010-01-23 08:46:32 +08:00
|
|
|
}
|