Improve access control diagnostics. Perform access control on member-pointer

conversions.  Fix an access-control bug where privileges were not considered
at intermediate points along the inheritance path.  Prepare for friends.

llvm-svn: 95775
This commit is contained in:
John McCall 2010-02-10 09:31:12 +00:00
parent f22553a1c7
commit 5b0829a321
20 changed files with 669 additions and 390 deletions

View File

@ -161,7 +161,8 @@ class CXXBasePaths {
void ComputeDeclsFound();
public:
typedef std::list<CXXBasePath>::const_iterator paths_iterator;
typedef std::list<CXXBasePath>::iterator paths_iterator;
typedef std::list<CXXBasePath>::const_iterator const_paths_iterator;
typedef NamedDecl **decl_iterator;
/// BasePaths - Construct a new BasePaths structure to record the
@ -175,8 +176,10 @@ public:
~CXXBasePaths() { delete [] DeclsFound; }
paths_iterator begin() const { return Paths.begin(); }
paths_iterator end() const { return Paths.end(); }
paths_iterator begin() { return Paths.begin(); }
paths_iterator end() { return Paths.end(); }
const_paths_iterator begin() const { return Paths.begin(); }
const_paths_iterator end() const { return Paths.end(); }
CXXBasePath& front() { return Paths.front(); }
const CXXBasePath& front() const { return Paths.front(); }
@ -206,7 +209,7 @@ public:
const RecordType* getDetectedVirtual() const {
return DetectedVirtual;
}
/// \brief Retrieve the type from which this base-paths search
/// began
CXXRecordDecl *getOrigin() const { return Origin; }

View File

@ -417,17 +417,20 @@ def err_deep_exception_specs_differ : Error<
// C++ access checking
def err_class_redeclared_with_different_access : Error<
"%0 redeclared with '%1' access">;
def err_access_private : Error<"%0 is a private member of %1">;
def err_access_ctor_private : Error<"calling a private constructor of %0">;
// Say something about the context for these?
def err_access_protected : Error<"%0 is a protected member of %1">;
def err_access_ctor_protected : Error<"calling a protected constructor of %0">;
def note_previous_access_declaration : Note<
"previously declared '%1' here">;
def err_access_outside_class : Error<
"access to %select{private|protected}0 member outside any class context">;
def note_access_natural : Note<"declared %select{private|protected}0 here">;
def note_access_natural : Note<
"%select{|implicitly }1declared %select{private|protected}0 here">;
def note_access_constrained_by_path : Note<
"access to decl constrained by %select{private|protected}0 inheritance">;
def err_access_protected : Error<
"access to protected member of %0 from %1, which is not a subclass">;
def err_access_private : Error<
"access to private member of %0 from %1">;
"constrained by %select{|implicitly }1%select{private|protected}0"
" inheritance here">;
// C++ name lookup
def err_incomplete_nested_name_spec : Error<
@ -509,9 +512,8 @@ def note_overridden_virtual_function : Note<
"overridden virtual function is here">;
def err_covariant_return_inaccessible_base : Error<
"return type of virtual function %2 is not covariant with the return type "
"of the function it overrides "
"(conversion from %0 to inaccessible base class %1)">, NoSFINAE;
"invalid covariant return for virtual function: %1 is a "
"%select{private|protected}2 base class of %0">, NoSFINAE;
def err_covariant_return_ambiguous_derived_to_base_conv : Error<
"return type of virtual function %3 is not covariant with the return type of "
"the function it overrides (ambiguous conversion from derived class "
@ -1958,7 +1960,9 @@ def err_ambiguous_base_to_derived_cast : Error<
def err_static_downcast_via_virtual : Error<
"cannot cast %0 to %1 via virtual base %2">;
def err_downcast_from_inaccessible_base : Error<
"cannot cast %1 to %0 due to inaccessible conversion path">;
"cannot cast %select{private|protected}2 base class %1 to %0">;
def err_upcast_to_inaccessible_base : Error<
"cannot cast %0 to its %select{private|protected}2 base class %1">;
def err_bad_dynamic_cast_not_ref_or_ptr : Error<
"%0 is not a reference or pointer">;
def err_bad_dynamic_cast_not_class : Error<"%0 is not a class">;

View File

@ -503,7 +503,7 @@ private:
if (isAmbiguous())
SemaRef.DiagnoseAmbiguousLookup(*this);
else if (isClassLookup() && SemaRef.getLangOptions().AccessControl)
SemaRef.CheckAccess(*this);
SemaRef.CheckLookupAccess(*this);
}
void setAmbiguous(AmbiguityKind AK) {

View File

@ -96,6 +96,7 @@ namespace clang {
class ObjCPropertyDecl;
class ObjCContainerDecl;
class FunctionProtoType;
class CXXBasePath;
class CXXBasePaths;
class CXXTemporary;
class LookupResult;
@ -275,6 +276,77 @@ public:
/// \brief All the tentative definitions encountered in the TU.
std::vector<VarDecl *> TentativeDefinitions;
/// An enum describing the kind of diagnostics to use when checking
/// access.
enum AccessDiagnosticsKind {
/// Suppress diagnostics.
ADK_quiet,
/// Use the normal diagnostics.
ADK_normal,
/// Use the diagnostics appropriate for checking a covariant
/// return type.
ADK_covariance
};
class AccessedEntity {
public:
enum Kind {
/// A member declaration found through lookup. The target is the
/// member.
Member,
/// A base-to-derived conversion. The target is the base class.
BaseToDerivedConversion,
/// A derived-to-base conversion. The target is the base class.
DerivedToBaseConversion
};
bool isMemberAccess() const { return K == Member; }
static AccessedEntity makeMember(CXXRecordDecl *NamingClass,
AccessSpecifier Access,
NamedDecl *Target) {
AccessedEntity E;
E.K = Member;
E.Access = Access;
E.Target = Target;
E.NamingClass = NamingClass;
return E;
}
static AccessedEntity makeBaseClass(bool BaseToDerived,
CXXRecordDecl *BaseClass,
CXXRecordDecl *DerivedClass,
AccessSpecifier Access) {
AccessedEntity E;
E.K = BaseToDerived ? BaseToDerivedConversion : DerivedToBaseConversion;
E.Access = Access;
E.Target = BaseClass;
E.NamingClass = DerivedClass;
return E;
}
Kind getKind() const { return Kind(K); }
AccessSpecifier getAccess() const { return AccessSpecifier(Access); }
// These apply to member decls...
NamedDecl *getTargetDecl() const { return Target; }
CXXRecordDecl *getNamingClass() const { return NamingClass; }
// ...and these apply to hierarchy conversions.
CXXRecordDecl *getBaseClass() const { return cast<CXXRecordDecl>(Target); }
CXXRecordDecl *getDerivedClass() const { return NamingClass; }
private:
unsigned K : 2;
unsigned Access : 2;
NamedDecl *Target;
CXXRecordDecl *NamingClass;
};
struct DelayedDiagnostic {
enum DDKind { Deprecation, Access };
@ -288,11 +360,7 @@ public:
struct { NamedDecl *Decl; } DeprecationData;
/// Access control.
struct {
NamedDecl *Decl;
AccessSpecifier Access;
CXXRecordDecl *NamingClass;
} AccessData;
AccessedEntity AccessData;
};
static DelayedDiagnostic makeDeprecation(SourceLocation Loc,
@ -306,16 +374,12 @@ public:
}
static DelayedDiagnostic makeAccess(SourceLocation Loc,
NamedDecl *Decl,
AccessSpecifier AS,
CXXRecordDecl *NamingClass) {
const AccessedEntity &Entity) {
DelayedDiagnostic DD;
DD.Kind = Access;
DD.Triggered = false;
DD.Loc = Loc;
DD.AccessData.Decl = Decl;
DD.AccessData.Access = AS;
DD.AccessData.NamingClass = NamingClass;
DD.AccessData = Entity;
return DD;
}
@ -2385,7 +2449,7 @@ public:
SourceLocation Loc, SourceRange Range,
bool IgnoreAccess = false);
bool CheckDerivedToBaseConversion(QualType Derived, QualType Base,
unsigned InaccessibleBaseID,
AccessDiagnosticsKind ADK,
unsigned AmbigiousBaseConvID,
SourceLocation Loc, SourceRange Range,
DeclarationName Name);
@ -2412,38 +2476,43 @@ public:
// C++ Access Control
//
enum AccessResult {
AR_accessible,
AR_inaccessible,
AR_dependent,
AR_delayed
};
bool SetMemberAccessSpecifier(NamedDecl *MemberDecl,
NamedDecl *PrevMemberDecl,
AccessSpecifier LexicalAS);
const CXXBaseSpecifier *FindInaccessibleBase(QualType Derived, QualType Base,
CXXBasePaths &Paths,
bool NoPrivileges = false);
bool CheckUnresolvedMemberAccess(UnresolvedMemberExpr *E,
NamedDecl *D,
AccessSpecifier Access);
bool CheckUnresolvedLookupAccess(UnresolvedLookupExpr *E,
NamedDecl *D,
AccessSpecifier Access);
bool CheckConstructorAccess(SourceLocation Loc, CXXConstructorDecl *D,
AccessSpecifier Access);
bool CheckDestructorAccess(SourceLocation Loc, const RecordType *Record);
bool CheckMemberOperatorAccess(SourceLocation Loc, Expr *ObjectExpr,
NamedDecl *D, AccessSpecifier Access);
bool CheckAccess(const LookupResult &R, NamedDecl *D, AccessSpecifier Access);
void CheckAccess(const LookupResult &R);
AccessResult CheckUnresolvedMemberAccess(UnresolvedMemberExpr *E,
NamedDecl *D,
AccessSpecifier Access);
AccessResult CheckUnresolvedLookupAccess(UnresolvedLookupExpr *E,
NamedDecl *D,
AccessSpecifier Access);
AccessResult CheckConstructorAccess(SourceLocation Loc,
CXXConstructorDecl *D,
AccessSpecifier Access);
AccessResult CheckDestructorAccess(SourceLocation Loc,
const RecordType *Record);
AccessResult CheckMemberOperatorAccess(SourceLocation Loc,
Expr *ObjectExpr,
NamedDecl *D,
AccessSpecifier Access);
AccessResult CheckBaseClassAccess(SourceLocation AccessLoc,
bool IsBaseToDerived,
QualType Base, QualType Derived,
const CXXBasePath &Path,
bool ForceCheck = false,
bool ForceUnprivileged = false,
AccessDiagnosticsKind ADK = ADK_normal);
void CheckLookupAccess(const LookupResult &R);
void HandleDelayedAccessCheck(DelayedDiagnostic &DD, Decl *Ctx);
bool CheckEffectiveAccess(DeclContext *EffectiveContext,
const LookupResult &R, NamedDecl *D,
AccessSpecifier Access);
bool CheckBaseClassAccess(QualType Derived, QualType Base,
unsigned InaccessibleBaseID,
CXXBasePaths& Paths, SourceLocation AccessLoc,
DeclarationName Name);
enum AbstractDiagSelID {
AbstractNone = -1,

View File

@ -49,326 +49,500 @@ bool Sema::SetMemberAccessSpecifier(NamedDecl *MemberDecl,
return false;
}
/// Find a class on the derivation path between Derived and Base that is
/// inaccessible. If @p NoPrivileges is true, special access rights (members
/// and friends) are not considered.
const CXXBaseSpecifier *Sema::FindInaccessibleBase(
QualType Derived, QualType Base, CXXBasePaths &Paths, bool NoPrivileges) {
Base = Context.getCanonicalType(Base).getUnqualifiedType();
assert(!Paths.isAmbiguous(Base) &&
"Can't check base class access if set of paths is ambiguous");
assert(Paths.isRecordingPaths() &&
"Can't check base class access without recorded paths");
namespace {
struct EffectiveContext {
EffectiveContext() : Record(0), Function(0) {}
const CXXBaseSpecifier *InaccessibleBase = 0;
const CXXRecordDecl *CurrentClassDecl = 0;
if (CXXMethodDecl *MD = dyn_cast_or_null<CXXMethodDecl>(getCurFunctionDecl()))
CurrentClassDecl = MD->getParent();
for (CXXBasePaths::paths_iterator Path = Paths.begin(), PathsEnd = Paths.end();
Path != PathsEnd; ++Path) {
bool FoundInaccessibleBase = false;
for (CXXBasePath::const_iterator Element = Path->begin(),
ElementEnd = Path->end(); Element != ElementEnd; ++Element) {
const CXXBaseSpecifier *Base = Element->Base;
switch (Base->getAccessSpecifier()) {
default:
assert(0 && "invalid access specifier");
case AS_public:
// Nothing to do.
break;
case AS_private:
// FIXME: Check if the current function/class is a friend.
if (NoPrivileges || CurrentClassDecl != Element->Class)
FoundInaccessibleBase = true;
break;
case AS_protected:
// FIXME: Implement
break;
}
if (FoundInaccessibleBase) {
InaccessibleBase = Base;
break;
}
}
if (!FoundInaccessibleBase) {
// We found a path to the base, our work here is done.
return 0;
}
}
assert(InaccessibleBase && "no path found, but no inaccessible base");
return InaccessibleBase;
}
/// CheckBaseClassAccess - Check that a derived class can access its base class
/// and report an error if it can't. [class.access.base]
bool Sema::CheckBaseClassAccess(QualType Derived, QualType Base,
unsigned InaccessibleBaseID,
CXXBasePaths &Paths, SourceLocation AccessLoc,
DeclarationName Name) {
if (!getLangOptions().AccessControl)
return false;
const CXXBaseSpecifier *InaccessibleBase = FindInaccessibleBase(
Derived, Base, Paths);
if (InaccessibleBase) {
Diag(AccessLoc, InaccessibleBaseID)
<< Derived << Base << Name;
AccessSpecifier AS = InaccessibleBase->getAccessSpecifierAsWritten();
// If there's no written access specifier, then the inheritance specifier
// is implicitly private.
if (AS == AS_none)
Diag(InaccessibleBase->getSourceRange().getBegin(),
diag::note_inheritance_implicitly_private_here);
explicit EffectiveContext(DeclContext *DC) {
if (isa<FunctionDecl>(DC)) {
Function = cast<FunctionDecl>(DC);
DC = Function->getDeclContext();
} else
Function = 0;
if (isa<CXXRecordDecl>(DC))
Record = cast<CXXRecordDecl>(DC)->getCanonicalDecl();
else
Diag(InaccessibleBase->getSourceRange().getBegin(),
diag::note_inheritance_specifier_here) << AS;
return true;
Record = 0;
}
return false;
bool isClass(const CXXRecordDecl *R) const {
return R->getCanonicalDecl() == Record;
}
CXXRecordDecl *Record;
FunctionDecl *Function;
};
}
/// Diagnose the path which caused the given declaration to become
/// inaccessible.
static void DiagnoseAccessPath(Sema &S, const LookupResult &R, NamedDecl *D,
AccessSpecifier Access) {
static CXXRecordDecl *FindDeclaringClass(NamedDecl *D) {
CXXRecordDecl *DeclaringClass = cast<CXXRecordDecl>(D->getDeclContext());
while (DeclaringClass->isAnonymousStructOrUnion())
DeclaringClass = cast<CXXRecordDecl>(DeclaringClass->getDeclContext());
return DeclaringClass;
}
static Sema::AccessResult GetFriendKind(Sema &S,
const EffectiveContext &EC,
const CXXRecordDecl *Class) {
if (EC.isClass(Class))
return Sema::AR_accessible;
// FIXME: implement
return Sema::AR_inaccessible;
}
/// Finds the best path from the naming class to the declaring class,
/// taking friend declarations into account.
///
/// \return null if friendship is dependent
static CXXBasePath *FindBestPath(Sema &S,
const EffectiveContext &EC,
CXXRecordDecl *Derived,
CXXRecordDecl *Base,
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;
// 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.
AccessSpecifier PathAccess = AS_public;
CXXBasePath::iterator I = PI->end(), E = PI->begin();
while (I != E) {
--I;
AccessSpecifier BaseAccess = I->Base->getAccessSpecifier();
if (BaseAccess != AS_public) {
switch (GetFriendKind(S, EC, I->Class)) {
case Sema::AR_inaccessible: break;
case Sema::AR_accessible: BaseAccess = AS_public; break;
case Sema::AR_dependent: return 0;
case Sema::AR_delayed:
llvm_unreachable("friend resolution is never delayed"); break;
}
}
PathAccess = CXXRecordDecl::MergeAccess(BaseAccess, PathAccess);
}
// 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;
}
}
return BestPath;
}
/// 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.
if (Access == D->getAccess() || D->getAccess() == AS_private) {
S.Diag(D->getLocation(), diag::note_access_natural)
<< (unsigned) (Access == AS_protected);
// 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;
}
case Sema::AR_accessible: break;
case Sema::AR_dependent:
case Sema::AR_delayed:
llvm_unreachable("dependent/delayed not allowed");
return;
}
}
CXXBasePaths Paths;
CXXBasePath &Path = *FindBestPath(S, EC, NamingClass, DeclaringClass, Paths);
CXXBasePath::iterator I = Path.end(), E = Path.begin();
while (I != E) {
--I;
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;
}
}
llvm_unreachable("access not apparently constrained by path");
}
/// 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);
if (isa<CXXConstructorDecl>(D)) {
unsigned DiagID = (Access == AS_protected ? diag::err_access_ctor_protected
: diag::err_access_ctor_private);
S.Diag(Loc, DiagID)
<< S.Context.getTypeDeclType(DeclaringClass);
} else {
unsigned DiagID = (Access == AS_protected ? diag::err_access_protected
: diag::err_access_private);
S.Diag(Loc, DiagID)
<< D->getDeclName()
<< S.Context.getTypeDeclType(DeclaringClass);
}
DiagnoseAccessPath(S, EC, NamingClass, DeclaringClass, D, Access);
}
/// Diagnose an inaccessible hierarchy conversion.
static void DiagnoseInaccessibleBase(Sema &S, SourceLocation Loc,
const EffectiveContext &EC,
AccessSpecifier Access,
const Sema::AccessedEntity &Entity,
Sema::AccessDiagnosticsKind ADK) {
if (ADK == Sema::ADK_covariance) {
S.Diag(Loc, diag::err_covariant_return_inaccessible_base)
<< S.Context.getTypeDeclType(Entity.getDerivedClass())
<< S.Context.getTypeDeclType(Entity.getBaseClass())
<< (Access == AS_protected);
} else if (Entity.getKind() == Sema::AccessedEntity::BaseToDerivedConversion) {
S.Diag(Loc, diag::err_downcast_from_inaccessible_base)
<< S.Context.getTypeDeclType(Entity.getDerivedClass())
<< S.Context.getTypeDeclType(Entity.getBaseClass())
<< (Access == AS_protected);
} else {
S.Diag(Loc, diag::err_upcast_to_inaccessible_base)
<< S.Context.getTypeDeclType(Entity.getDerivedClass())
<< S.Context.getTypeDeclType(Entity.getBaseClass())
<< (Access == AS_protected);
}
DiagnoseAccessPath(S, EC, Entity.getDerivedClass(),
Entity.getBaseClass(), 0, Access);
}
static void DiagnoseBadAccess(Sema &S,
SourceLocation Loc,
const EffectiveContext &EC,
CXXRecordDecl *NamingClass,
AccessSpecifier Access,
const Sema::AccessedEntity &Entity,
Sema::AccessDiagnosticsKind ADK) {
if (Entity.isMemberAccess())
DiagnoseInaccessibleMember(S, Loc, EC, NamingClass, Access, Entity);
else
DiagnoseInaccessibleBase(S, Loc, EC, Access, Entity, ADK);
}
/// Try to elevate access using friend declarations. This is
/// potentially quite expensive.
static void TryElevateAccess(Sema &S,
const EffectiveContext &EC,
const Sema::AccessedEntity &Entity,
AccessSpecifier &Access) {
CXXRecordDecl *DeclaringClass;
if (Entity.isMemberAccess()) {
DeclaringClass = FindDeclaringClass(Entity.getTargetDecl());
} else {
DeclaringClass = Entity.getBaseClass();
}
CXXRecordDecl *NamingClass = Entity.getNamingClass();
// Adjust the declaration of the referred entity.
AccessSpecifier DeclAccess = AS_none;
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;
case Sema::AR_dependent: /* FIXME: delay dependent friendship */ return;
case Sema::AR_delayed: llvm_unreachable("friend status is never delayed");
}
}
if (DeclaringClass == NamingClass) {
Access = DeclAccess;
return;
}
}
assert(DeclaringClass != NamingClass);
// Append the declaration's access if applicable.
CXXBasePaths Paths;
CXXBasePath *Path = FindBestPath(S, EC, Entity.getNamingClass(),
DeclaringClass, Paths);
if (!Path) {
// FIXME: delay dependent friendship
return;
}
// TODO: flesh this out
S.Diag(D->getLocation(), diag::note_access_constrained_by_path)
<< (unsigned) (Access == AS_protected);
// Grab the access along the best path.
AccessSpecifier NewAccess = Path->Access;
if (Entity.isMemberAccess())
NewAccess = CXXRecordDecl::MergeAccess(NewAccess, DeclAccess);
assert(NewAccess <= Access && "access along best path worse than direct?");
Access = NewAccess;
}
/// Checks access to the given declaration in the current context.
///
/// \param R the means via which the access was made; must have a naming
/// class set
/// \param D the declaration accessed
/// \param Access the best access along any inheritance path from the
/// naming class to the declaration. AS_none means the path is impossible
bool Sema::CheckAccess(const LookupResult &R, NamedDecl *D,
AccessSpecifier Access) {
assert(R.getNamingClass() && "performing access check without naming class");
/// Checks access to an entity from the given effective context.
static Sema::AccessResult CheckEffectiveAccess(Sema &S,
const EffectiveContext &EC,
SourceLocation Loc,
Sema::AccessedEntity const &Entity,
Sema::AccessDiagnosticsKind ADK) {
AccessSpecifier Access = Entity.getAccess();
assert(Access != AS_public);
// If the access path is public, it's accessible everywhere.
if (Access == AS_public)
return false;
// 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 (CurContext->isFileContext() && ParsingDeclDepth) {
DelayedDiagnostics.push_back(
DelayedDiagnostic::makeAccess(R.getNameLoc(), D, Access,
R.getNamingClass()));
return false;
}
return CheckEffectiveAccess(CurContext, R, D, Access);
}
/// Checks access from the given effective context.
bool Sema::CheckEffectiveAccess(DeclContext *EffectiveContext,
const LookupResult &R,
NamedDecl *D, AccessSpecifier Access) {
DeclContext *DC = EffectiveContext;
while (isa<CXXRecordDecl>(DC) &&
cast<CXXRecordDecl>(DC)->isAnonymousStructOrUnion())
DC = DC->getParent();
CXXRecordDecl *CurRecord;
if (isa<CXXRecordDecl>(DC))
CurRecord = cast<CXXRecordDecl>(DC);
else if (isa<CXXMethodDecl>(DC))
CurRecord = cast<CXXMethodDecl>(DC)->getParent();
else {
Diag(R.getNameLoc(), diag::err_access_outside_class)
<< (Access == AS_protected);
DiagnoseAccessPath(*this, R, D, Access);
return true;
}
CXXRecordDecl *NamingClass = R.getNamingClass();
CXXRecordDecl *NamingClass = Entity.getNamingClass();
while (NamingClass->isAnonymousStructOrUnion())
// This should be guaranteed by the fact that the decl has
// non-public access. If not, we should make it guaranteed!
NamingClass = cast<CXXRecordDecl>(NamingClass);
if (!EC.Record) {
TryElevateAccess(S, EC, Entity, Access);
if (Access == AS_public) return Sema::AR_accessible;
if (ADK != Sema::ADK_quiet)
DiagnoseBadAccess(S, Loc, EC, NamingClass, Access, Entity, ADK);
return Sema::AR_inaccessible;
}
// White-list accesses from within the declaring class.
if (Access != AS_none &&
CurRecord->getCanonicalDecl() == NamingClass->getCanonicalDecl())
return false;
if (Access != AS_none && EC.isClass(NamingClass))
return Sema::AR_accessible;
// If the access is worse than 'protected', try to promote to it using
// friend declarations.
bool TriedElevation = false;
if (Access != AS_protected) {
TryElevateAccess(S, EC, Entity, Access);
if (Access == AS_public) return Sema::AR_accessible;
TriedElevation = true;
}
// Protected access.
if (Access == AS_protected) {
// FIXME: implement [class.protected]p1
if (CurRecord->isDerivedFrom(NamingClass))
return false;
if (EC.Record->isDerivedFrom(NamingClass))
return Sema::AR_accessible;
// FIXME: dependent classes
// FIXME: delay dependent classes
}
// FIXME: friends
// We're about to reject; one last chance to promote access.
if (!TriedElevation) {
TryElevateAccess(S, EC, Entity, Access);
if (Access == AS_public) return Sema::AR_accessible;
}
// Okay, that's it, reject it.
if (ADK != Sema::ADK_quiet)
DiagnoseBadAccess(S, Loc, EC, NamingClass, Access, Entity, ADK);
return Sema::AR_inaccessible;
}
// Okay, it's a bad access, reject it.
static Sema::AccessResult CheckAccess(Sema &S, SourceLocation Loc,
const Sema::AccessedEntity &Entity,
Sema::AccessDiagnosticsKind ADK
= Sema::ADK_normal) {
// If the access path is public, it's accessible everywhere.
if (Entity.getAccess() == AS_public)
return Sema::AR_accessible;
CXXRecordDecl *DeclaringClass = cast<CXXRecordDecl>(D->getDeclContext());
if (Access == AS_protected) {
Diag(R.getNameLoc(), diag::err_access_protected)
<< Context.getTypeDeclType(DeclaringClass)
<< Context.getTypeDeclType(CurRecord);
DiagnoseAccessPath(*this, R, D, Access);
return true;
// 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) {
assert(ADK == Sema::ADK_normal && "delaying abnormal access check");
S.DelayedDiagnostics.push_back(
Sema::DelayedDiagnostic::makeAccess(Loc, Entity));
return Sema::AR_delayed;
}
assert(Access == AS_private || Access == AS_none);
Diag(R.getNameLoc(), diag::err_access_private)
<< Context.getTypeDeclType(DeclaringClass)
<< Context.getTypeDeclType(CurRecord);
DiagnoseAccessPath(*this, R, D, Access);
return true;
return CheckEffectiveAccess(S, EffectiveContext(S.CurContext),
Loc, Entity, ADK);
}
void Sema::HandleDelayedAccessCheck(DelayedDiagnostic &DD, Decl *Ctx) {
NamedDecl *D = DD.AccessData.Decl;
// Fake up a lookup result.
LookupResult R(*this, D->getDeclName(), DD.Loc, LookupOrdinaryName);
R.suppressDiagnostics();
R.setNamingClass(DD.AccessData.NamingClass);
// Pretend we did this from the context of the newly-parsed
// declaration.
DeclContext *EffectiveContext = Ctx->getDeclContext();
EffectiveContext EC(Ctx->getDeclContext());
if (CheckEffectiveAccess(EffectiveContext, R, D, DD.AccessData.Access))
if (CheckEffectiveAccess(*this, EC, DD.Loc, DD.AccessData, ADK_normal))
DD.Triggered = true;
}
bool Sema::CheckUnresolvedLookupAccess(UnresolvedLookupExpr *E,
NamedDecl *D, AccessSpecifier Access) {
Sema::AccessResult Sema::CheckUnresolvedLookupAccess(UnresolvedLookupExpr *E,
NamedDecl *D,
AccessSpecifier Access) {
if (!getLangOptions().AccessControl || !E->getNamingClass())
return false;
return AR_accessible;
// Fake up a lookup result.
LookupResult R(*this, E->getName(), E->getNameLoc(), LookupOrdinaryName);
R.suppressDiagnostics();
R.setNamingClass(E->getNamingClass());
R.addDecl(D, Access);
// FIXME: protected check (triggers for member-address expressions)
return CheckAccess(R, D, Access);
return CheckAccess(*this, E->getNameLoc(),
AccessedEntity::makeMember(E->getNamingClass(), Access, D));
}
/// Perform access-control checking on a previously-unresolved member
/// access which has now been resolved to a member.
bool Sema::CheckUnresolvedMemberAccess(UnresolvedMemberExpr *E,
NamedDecl *D, AccessSpecifier Access) {
Sema::AccessResult Sema::CheckUnresolvedMemberAccess(UnresolvedMemberExpr *E,
NamedDecl *D,
AccessSpecifier Access) {
if (!getLangOptions().AccessControl)
return false;
return AR_accessible;
// Fake up a lookup result.
LookupResult R(*this, E->getMemberName(), E->getMemberLoc(),
LookupOrdinaryName);
R.suppressDiagnostics();
R.setNamingClass(E->getNamingClass());
R.addDecl(D, Access);
if (CheckAccess(R, D, Access))
return true;
// FIXME: protected check
return false;
return CheckAccess(*this, E->getMemberLoc(),
AccessedEntity::makeMember(E->getNamingClass(), Access, D));
}
bool Sema::CheckDestructorAccess(SourceLocation Loc, const RecordType *RT) {
Sema::AccessResult Sema::CheckDestructorAccess(SourceLocation Loc,
const RecordType *RT) {
if (!getLangOptions().AccessControl)
return false;
return AR_accessible;
CXXRecordDecl *NamingClass = cast<CXXRecordDecl>(RT->getDecl());
CXXDestructorDecl *Dtor = NamingClass->getDestructor(Context);
AccessSpecifier Access = Dtor->getAccess();
if (Access == AS_public)
return false;
return AR_accessible;
LookupResult R(*this, Dtor->getDeclName(), Loc, LookupOrdinaryName);
R.suppressDiagnostics();
R.setNamingClass(NamingClass);
return CheckAccess(R, Dtor, Access);
// FIXME: protected check
return CheckAccess(*this, Loc,
AccessedEntity::makeMember(NamingClass, Access, Dtor));
}
/// Checks access to a constructor.
bool Sema::CheckConstructorAccess(SourceLocation UseLoc,
Sema::AccessResult Sema::CheckConstructorAccess(SourceLocation UseLoc,
CXXConstructorDecl *Constructor,
AccessSpecifier Access) {
if (!getLangOptions().AccessControl)
return false;
return AR_accessible;
CXXRecordDecl *NamingClass = cast<CXXRecordDecl>(Constructor->getParent());
LookupResult R(*this, Constructor->getDeclName(), UseLoc, LookupOrdinaryName);
R.suppressDiagnostics();
R.setNamingClass(NamingClass);
return CheckAccess(R, Constructor, Access);
CXXRecordDecl *NamingClass = Constructor->getParent();
return CheckAccess(*this, UseLoc,
AccessedEntity::makeMember(NamingClass, Access, Constructor));
}
/// Checks access to an overloaded member operator, including
/// conversion operators.
bool Sema::CheckMemberOperatorAccess(SourceLocation OpLoc,
Expr *ObjectExpr,
NamedDecl *MemberOperator,
AccessSpecifier Access) {
Sema::AccessResult Sema::CheckMemberOperatorAccess(SourceLocation OpLoc,
Expr *ObjectExpr,
NamedDecl *MemberOperator,
AccessSpecifier Access) {
if (!getLangOptions().AccessControl)
return false;
return AR_accessible;
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());
LookupResult R(*this, DeclarationName(), OpLoc, LookupOrdinaryName);
R.suppressDiagnostics();
return CheckAccess(*this, OpLoc,
AccessedEntity::makeMember(NamingClass, Access, MemberOperator));
}
R.setNamingClass(NamingClass);
if (CheckAccess(R, MemberOperator, Access))
return true;
/// 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,
bool IsBaseToDerived,
QualType Base,
QualType Derived,
const CXXBasePath &Path,
bool ForceCheck,
bool ForceUnprivileged,
AccessDiagnosticsKind ADK) {
if (!ForceCheck && !getLangOptions().AccessControl)
return AR_accessible;
// FIXME: protected check
if (Path.Access == AS_public)
return AR_accessible;
return false;
// TODO: preserve the information about which types exactly were used.
CXXRecordDecl *BaseD, *DerivedD;
BaseD = cast<CXXRecordDecl>(Base->getAs<RecordType>()->getDecl());
DerivedD = cast<CXXRecordDecl>(Derived->getAs<RecordType>()->getDecl());
AccessedEntity Entity = AccessedEntity::makeBaseClass(IsBaseToDerived,
BaseD, DerivedD,
Path.Access);
if (ForceUnprivileged)
return CheckEffectiveAccess(*this, EffectiveContext(),
AccessLoc, Entity, ADK);
return CheckAccess(*this, AccessLoc, Entity, ADK);
}
/// Checks access to all the declarations in the given result set.
void Sema::CheckAccess(const LookupResult &R) {
void Sema::CheckLookupAccess(const LookupResult &R) {
assert(getLangOptions().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)
CheckAccess(R, *I, I.getAccess());
if (I.getAccess() != AS_public)
CheckAccess(*this, R.getNameLoc(),
AccessedEntity::makeMember(R.getNamingClass(),
I.getAccess(), *I));
}

View File

@ -778,9 +778,10 @@ TryStaticDowncast(Sema &Self, CanQualType SrcType, CanQualType DestType,
return TC_Failed;
}
if (!CStyle && Self.CheckBaseClassAccess(DestType, SrcType,
diag::err_downcast_from_inaccessible_base, Paths,
OpRange.getBegin(), DeclarationName())) {
if (!CStyle && Self.CheckBaseClassAccess(OpRange.getBegin(),
/*IsBaseToDerived*/ true,
SrcType, DestType,
Paths.front())) {
msg = 0;
return TC_Failed;
}
@ -844,9 +845,10 @@ TryStaticMemberPointerUpcast(Sema &Self, QualType SrcType, QualType DestType,
return TC_Failed;
}
if (!CStyle && Self.CheckBaseClassAccess(DestType, SrcType,
diag::err_downcast_from_inaccessible_base, Paths,
OpRange.getBegin(), DeclarationName())) {
if (!CStyle && Self.CheckBaseClassAccess(OpRange.getBegin(),
/*IsBaseToDerived*/ false,
DestType, SrcType,
Paths.front())) {
msg = 0;
return TC_Failed;
}

View File

@ -713,7 +713,7 @@ bool Sema::IsDerivedFrom(QualType Derived, QualType Base, CXXBasePaths &Paths) {
/// if there is an error.
bool
Sema::CheckDerivedToBaseConversion(QualType Derived, QualType Base,
unsigned InaccessibleBaseID,
AccessDiagnosticsKind ADK,
unsigned AmbigiousBaseConvID,
SourceLocation Loc, SourceRange Range,
DeclarationName Name) {
@ -729,11 +729,20 @@ Sema::CheckDerivedToBaseConversion(QualType Derived, QualType Base,
(void)DerivationOkay;
if (!Paths.isAmbiguous(Context.getCanonicalType(Base).getUnqualifiedType())) {
if (InaccessibleBaseID == 0)
if (ADK == ADK_quiet)
return false;
// Check that the base class can be accessed.
return CheckBaseClassAccess(Derived, Base, InaccessibleBaseID, Paths, Loc,
Name);
switch (CheckBaseClassAccess(Loc, /*IsBaseToDerived*/ false,
Base, Derived, Paths.front(),
/*force*/ false,
/*unprivileged*/ false,
ADK)) {
case AR_accessible: return false;
case AR_inaccessible: return true;
case AR_dependent: return false;
case AR_delayed: return false;
}
}
// We know that the derived-to-base conversion is ambiguous, and
@ -764,8 +773,7 @@ Sema::CheckDerivedToBaseConversion(QualType Derived, QualType Base,
SourceLocation Loc, SourceRange Range,
bool IgnoreAccess) {
return CheckDerivedToBaseConversion(Derived, Base,
IgnoreAccess ? 0 :
diag::err_conv_to_inaccessible_base,
IgnoreAccess ? ADK_quiet : ADK_normal,
diag::err_ambiguous_derived_to_base_conv,
Loc, Range, DeclarationName());
}
@ -5626,8 +5634,7 @@ bool Sema::CheckOverridingFunctionReturnType(const CXXMethodDecl *New,
}
// Check if we the conversion from derived to base is valid.
if (CheckDerivedToBaseConversion(NewClassTy, OldClassTy,
diag::err_covariant_return_inaccessible_base,
if (CheckDerivedToBaseConversion(NewClassTy, OldClassTy, ADK_covariance,
diag::err_covariant_return_ambiguous_derived_to_base_conv,
// FIXME: Should this point to the return type?
New->getLocation(), SourceRange(), New->getDeclName())) {

View File

@ -232,8 +232,22 @@ bool Sema::CheckExceptionSpecSubset(
if (Paths.isAmbiguous(CanonicalSuperT))
continue;
if (FindInaccessibleBase(CanonicalSubT, CanonicalSuperT, Paths, true))
continue;
// Do this check from a context without privileges.
switch (CheckBaseClassAccess(SourceLocation(), false,
CanonicalSuperT, CanonicalSubT,
Paths.front(),
/*ForceCheck*/ true,
/*ForceUnprivileged*/ true,
ADK_quiet)) {
case AR_accessible: break;
case AR_inaccessible: continue;
case AR_dependent:
llvm_unreachable("access check dependent for unprivileged context");
break;
case AR_delayed:
llvm_unreachable("access check delayed in non-declaration");
break;
}
Contained = true;
break;

View File

@ -404,7 +404,7 @@ void LookupResult::resolveKind() {
}
void LookupResult::addDeclsFromBasePaths(const CXXBasePaths &P) {
CXXBasePaths::paths_iterator I, E;
CXXBasePaths::const_paths_iterator I, E;
DeclContext::lookup_iterator DI, DE;
for (I = P.begin(), E = P.end(); I != E; ++I)
for (llvm::tie(DI,DE) = I->Decls; DI != DE; ++DI)

View File

@ -1355,14 +1355,13 @@ bool Sema::IsMemberPointerConversion(Expr *From, QualType FromType,
/// CheckMemberPointerConversion - Check the member pointer conversion from the
/// expression From to the type ToType. This routine checks for ambiguous or
/// virtual (FIXME: or inaccessible) base-to-derived member pointer conversions
/// virtual or inaccessible base-to-derived member pointer conversions
/// for which IsMemberPointerConversion has already returned true. It returns
/// true and produces a diagnostic if there was an error, or returns false
/// otherwise.
bool Sema::CheckMemberPointerConversion(Expr *From, QualType ToType,
CastExpr::CastKind &Kind,
bool IgnoreBaseAccess) {
(void)IgnoreBaseAccess;
QualType FromType = From->getType();
const MemberPointerType *FromPtrType = FromType->getAs<MemberPointerType>();
if (!FromPtrType) {
@ -1385,7 +1384,7 @@ bool Sema::CheckMemberPointerConversion(Expr *From, QualType ToType,
assert(FromClass->isRecordType() && "Pointer into non-class.");
assert(ToClass->isRecordType() && "Pointer into non-class.");
CXXBasePaths Paths(/*FindAmbiguities=*/true, /*RecordPaths=*/false,
CXXBasePaths Paths(/*FindAmbiguities=*/true, /*RecordPaths=*/ true,
/*DetectVirtual=*/true);
bool DerivationOkay = IsDerivedFrom(ToClass, FromClass, Paths);
assert(DerivationOkay &&
@ -1394,13 +1393,6 @@ bool Sema::CheckMemberPointerConversion(Expr *From, QualType ToType,
if (Paths.isAmbiguous(Context.getCanonicalType(FromClass).
getUnqualifiedType())) {
// Derivation is ambiguous. Redo the check to find the exact paths.
Paths.clear();
Paths.setRecordingPaths(true);
bool StillOkay = IsDerivedFrom(ToClass, FromClass, Paths);
assert(StillOkay && "Derivation changed due to quantum fluctuation.");
(void)StillOkay;
std::string PathDisplayStr = getAmbiguousPathsDisplayString(Paths);
Diag(From->getExprLoc(), diag::err_ambiguous_memptr_conv)
<< 0 << FromClass << ToClass << PathDisplayStr << From->getSourceRange();
@ -1414,6 +1406,10 @@ bool Sema::CheckMemberPointerConversion(Expr *From, QualType ToType,
return true;
}
if (!IgnoreBaseAccess)
CheckBaseClassAccess(From->getExprLoc(), /*BaseToDerived*/ true,
FromClass, ToClass, Paths.front());
// Must be a base to derived member conversion.
Kind = CastExpr::CK_BaseToDerivedMemberPointer;
return false;

View File

@ -54,13 +54,13 @@ namespace test0 {
// of the base class are accessible as protected members of the
// derived class.
namespace test1 {
class Base { // expected-note 6 {{constrained by protected inheritance}}
public: int pub; static int spub; // expected-note 2 {{constrained by protected inheritance}}
class Base {
public: int pub; static int spub;
protected: int prot; static int sprot; // expected-note 4 {{declared protected here}}
private: int priv; static int spriv; // expected-note 8 {{declared private here}}
};
class Test : protected Base {
class Test : protected Base { // expected-note 6 {{declared protected here}} expected-note 8 {{constrained by protected inheritance here}}
void test() {
pub++;
spub++;
@ -79,19 +79,19 @@ namespace test1 {
};
void test(Test *t) {
t->pub++; // expected-error {{protected member}}
t->pub++; // expected-error {{protected member}} expected-error {{protected base class}}
t->spub++; // expected-error {{protected member}}
t->prot++; // expected-error {{protected member}}
t->prot++; // expected-error {{protected member}} expected-error {{protected base class}}
t->sprot++; // expected-error {{protected member}}
t->priv++; // expected-error {{private member}}
t->priv++; // expected-error {{private member}} expected-error {{protected base class}}
t->spriv++; // expected-error {{private member}}
// Two possible errors here: one for Base, one for the member
t->Base::pub++; // expected-error {{protected member}}
t->Base::pub++; // expected-error {{protected member}} expected-error {{protected base class}}
t->Base::spub++; // expected-error {{protected member}}
t->Base::prot++; // expected-error 2 {{protected member}}
t->Base::prot++; // expected-error 2 {{protected member}} expected-error {{protected base class}}
t->Base::sprot++; // expected-error 2 {{protected member}}
t->Base::priv++; // expected-error {{protected member}} expected-error {{private member}}
t->Base::priv++; // expected-error {{protected member}} expected-error {{private member}} expected-error {{protected base class}}
t->Base::spriv++; // expected-error {{protected member}} expected-error {{private member}}
}
}
@ -102,21 +102,20 @@ namespace test1 {
// the base class are accessible as private members of the derived
// class.
namespace test2 {
class Base { //expected-note 6 {{constrained by private inheritance}}
class Base {
public:
int pub; // expected-note {{constrained by private inheritance}}
static int spub; // expected-note {{constrained by private inheritance}}
int pub;
static int spub;
protected:
int prot; // expected-note {{constrained by private inheritance}} \
// expected-note {{declared protected here}}
static int sprot; // expected-note {{constrained by private inheritance}} \
// expected-note {{declared protected here}}
int prot; // expected-note {{declared protected here}}
static int sprot; // expected-note {{declared protected here}}
private:
int priv; // expected-note 4 {{declared private here}}
static int spriv; // expected-note 4 {{declared private here}}
};
class Test : private Base { // expected-note 6 {{'private' inheritance specifier here}}
class Test : private Base { // expected-note 6 {{declared private here}} \
// expected-note 10 {{constrained by private inheritance here}}
void test() {
pub++;
spub++;
@ -135,18 +134,18 @@ namespace test2 {
};
void test(Test *t) {
t->pub++; // expected-error {{private member}} expected-error {{inaccessible base class}}
t->pub++; // expected-error {{private member}} expected-error {{private base class}}
t->spub++; // expected-error {{private member}}
t->prot++; // expected-error {{private member}} expected-error {{inaccessible base class}}
t->prot++; // expected-error {{private member}} expected-error {{private base class}}
t->sprot++; // expected-error {{private member}}
t->priv++; // expected-error {{private member}} expected-error {{inaccessible base class}}
t->priv++; // expected-error {{private member}} expected-error {{private base class}}
t->spriv++; // expected-error {{private member}}
t->Base::pub++; // expected-error {{private member}} expected-error {{inaccessible base class}}
t->Base::pub++; // expected-error {{private member}} expected-error {{private base class}}
t->Base::spub++; // expected-error {{private member}}
t->Base::prot++; // expected-error {{protected member}} expected-error {{private member}} expected-error {{inaccessible base class}}
t->Base::prot++; // expected-error {{protected member}} expected-error {{private member}} expected-error {{private base class}}
t->Base::sprot++; // expected-error {{protected member}} expected-error {{private member}}
t->Base::priv++; // expected-error 2 {{private member}} expected-error {{inaccessible base class}}
t->Base::priv++; // expected-error 2 {{private member}} expected-error {{private base class}}
t->Base::spriv++; // expected-error 2 {{private member}}
}
}

View File

@ -23,12 +23,12 @@ namespace test0 {
void test(A *op) {
op->foo(PublicInst);
op->foo(ProtectedInst); // expected-error {{access to protected member outside any class}}
op->foo(PrivateInst); // expected-error {{access to private member outside any class}}
op->foo(ProtectedInst); // expected-error {{'foo' is a protected member}}
op->foo(PrivateInst); // expected-error {{'foo' is a private member}}
void (A::*a)(Public&) = &A::foo;
void (A::*b)(Protected&) = &A::foo; // expected-error {{access to protected member outside any class}}
void (A::*c)(Private&) = &A::foo; // expected-error {{access to private member outside any class}}
void (A::*b)(Protected&) = &A::foo; // expected-error {{'foo' is a protected member}}
void (A::*c)(Private&) = &A::foo; // expected-error {{'foo' is a private member}}
}
}
@ -62,15 +62,15 @@ namespace test1 {
void test(A &a, Public &pub, Protected &prot, Private &priv) {
a + pub;
a + prot; // expected-error {{access to protected member}}
a + priv; // expected-error {{access to private member}}
a + prot; // expected-error {{'operator+' is a protected member}}
a + priv; // expected-error {{'operator+' is a private member}}
a[pub];
a[prot]; // expected-error {{access to protected member}}
a[priv]; // expected-error {{access to private member}}
a[prot]; // expected-error {{'operator[]' is a protected member}}
a[priv]; // expected-error {{'operator[]' is a private member}}
a(pub);
a(prot); // expected-error {{access to protected member}}
a(priv); // expected-error {{access to private member}}
-a; // expected-error {{access to private member}}
a(prot); // expected-error {{'operator()' is a protected member}}
a(priv); // expected-error {{'operator()' is a private member}}
-a; // expected-error {{'operator-' is a private member}}
const A &ca = a;
ca + pub;
@ -79,8 +79,8 @@ namespace test1 {
-ca;
// These are all surrogate calls
ca(pub);
ca(prot); // expected-error {{access to protected member}}
ca(priv); // expected-error {{access to private member}}
ca(prot); // expected-error {{'operator void (*)(class Protected &)' is a protected member}}
ca(priv); // expected-error {{'operator void (*)(class Private &)' is a private member}}
}
}
@ -93,7 +93,7 @@ namespace test2 {
static A foo;
};
A a; // expected-error {{access to private member}}
A a; // expected-error {{calling a private constructor}}
A A::foo; // okay
}
@ -105,10 +105,10 @@ namespace test3 {
static A foo;
};
A a; // expected-error {{access to private member}}
A a; // expected-error {{'~A' is a private member}}
A A::foo;
void foo(A param) { // expected-error {{access to private member}}
A local; // expected-error {{access to private member}}
void foo(A param) { // expected-error {{'~A' is a private member}}
A local; // expected-error {{'~A' is a private member}}
}
}

View File

@ -23,7 +23,7 @@ namespace test0 {
type foo();
};
A::type foo() { } // expected-error {{access to private member}}
A::type foo() { } // expected-error {{'type' is a private member}}
A::type A::foo() { }
}
@ -45,10 +45,10 @@ namespace test1 {
void test() {
A a;
Public pub = a;
Protected prot = a; // expected-error {{access to protected member}}
Private priv = a; // expected-error {{access to private member}}
Protected prot = a; // expected-error {{'operator Protected' is a protected member}}
Private priv = a; // expected-error {{'operator Private' is a private member}}
A apub = pub;
A aprot = prot; // expected-error {{access to protected member}}
A apriv = priv; // expected-error {{access to private member}}
A aprot = prot; // expected-error {{protected constructor}}
A apriv = priv; // expected-error {{private constructor}}
}
}

View File

@ -3,9 +3,9 @@
struct A { };
void f() {
struct B : private A {}; // expected-note{{'private' inheritance specifier here}}
struct B : private A {}; // expected-note{{declared private here}}
B b;
A *a = &b; // expected-error{{conversion from 'struct B' to inaccessible base class 'struct A'}}
A *a = &b; // expected-error{{cannot cast 'struct B' to its private base class 'struct A'}}
}

View File

@ -15,12 +15,12 @@ namespace test0 {
}
}
// FIXME: can't be inaccessible.
// Can't be inaccessible.
namespace test1 {
struct Derived : private Base {};
struct Derived : private Base {}; // expected-note 2 {{declared private here}}
void test() {
int (Derived::*d) = data_ptr; // error
int (Derived::*m)() = method_ptr; // error
int (Derived::*d) = data_ptr; // expected-error {{cannot cast private base class 'struct Base' to 'struct test1::Derived'}}
int (Derived::*m)() = method_ptr; // expected-error {{cannot cast private base class 'struct Base' to 'struct test1::Derived'}}
}
};

View File

@ -2,10 +2,10 @@
namespace T1 {
class A { };
class B : private A { }; // expected-note {{'private' inheritance specifier here}}
class B : private A { }; // expected-note {{declared private here}}
void f(B* b) {
A *a = b; // expected-error{{conversion from 'class T1::B' to inaccessible base class 'class T1::A'}}
A *a = b; // expected-error{{cannot cast 'class T1::B' to its private base class 'class T1::A'}}
}
}
@ -13,10 +13,10 @@ void f(B* b) {
namespace T2 {
class A { };
class B : A { }; // expected-note {{inheritance is implicitly 'private'}}
class B : A { }; // expected-note {{implicitly declared private here}}
void f(B* b) {
A *a = b; // expected-error {{conversion from 'class T2::B' to inaccessible base class 'class T2::A'}}
A *a = b; // expected-error {{cannot cast 'class T2::B' to its private base class 'class T2::A'}}
}
}
@ -63,13 +63,13 @@ namespace T6 {
class A {};
class B : private A { // expected-note {{'private' inheritance specifier here}}
class B : private A { // expected-note {{declared private here}}
void f(C* c);
};
class C : public B {
void f(C *c) {
A* a = c; // expected-error {{conversion from 'class T6::C' to inaccessible base class 'class T6::A'}}
A* a = c; // expected-error {{cannot cast 'class T6::C' to its private base class 'class T6::A'}}
}
};
@ -77,3 +77,14 @@ namespace T6 {
A *a = c;
}
}
namespace T7 {
class A {};
class B : public A {};
class C : private B {
void f(C *c) {
A* a = c; // okay
}
};
}

View File

@ -11,5 +11,5 @@ class P {
class N : M,P {
N() {}
int PR() { return iP + PPR(); } // expected-error 2 {{access to private member of 'class P'}}
int PR() { return iP + PPR(); } // expected-error 2 {{private member of 'class P'}}
};

View File

@ -25,7 +25,7 @@ struct Derived : Base {
void fn2();
};
struct Convertible { operator Base&(); };
struct Priv : private Base {}; // expected-note 4 {{'private' inheritance specifier here}}
struct Priv : private Base {}; // expected-note 4 {{declared private here}}
struct Mid : Base {};
struct Fin : Mid, Derived {};
typedef void (Derived::*DFnPtr)();
@ -111,12 +111,12 @@ void test()
Priv priv;
Fin fin;
(void)(i1 ? Base() : Priv()); // expected-error{{conversion from 'struct Priv' to inaccessible base class 'struct Base'}}
(void)(i1 ? Priv() : Base()); // expected-error{{error: conversion from 'struct Priv' to inaccessible base class 'struct Base'}}
(void)(i1 ? Base() : Priv()); // expected-error{{private base class}}
(void)(i1 ? Priv() : Base()); // expected-error{{private base class}}
(void)(i1 ? Base() : Fin()); // expected-error{{ambiguous conversion from derived class 'struct Fin' to base class 'struct Base'}}
(void)(i1 ? Fin() : Base()); // expected-error{{ambiguous conversion from derived class 'struct Fin' to base class 'struct Base'}}
(void)(i1 ? base : priv); // expected-error {{conversion from 'struct Priv' to inaccessible base class 'struct Base'}}
(void)(i1 ? priv : base); // expected-error {{conversion from 'struct Priv' to inaccessible base class 'struct Base'}}
(void)(i1 ? base : priv); // expected-error {{private base class}}
(void)(i1 ? priv : base); // expected-error {{private base class}}
(void)(i1 ? base : fin); // expected-error {{ambiguous conversion from derived class 'struct Fin' to base class 'struct Base'}}
(void)(i1 ? fin : base); // expected-error {{ambiguous conversion from derived class 'struct Fin' to base class 'struct Base'}}

View File

@ -4,7 +4,7 @@ struct B : public A {}; // Single public base.
struct C1 : public virtual B {}; // Single virtual base.
struct C2 : public virtual B {};
struct D : public C1, public C2 {}; // Diamond
struct E : private A {}; // Single private base. expected-note 3 {{'private' inheritance specifier here}}
struct E : private A {}; // Single private base. expected-note 3 {{declared private here}}
struct F : public C1 {}; // Single path to B with virtual.
struct G1 : public B {};
struct G2 : public B {};
@ -56,7 +56,7 @@ void t_529_2()
// Bad code below
(void)static_cast<void*>((const int*)0); // expected-error {{static_cast from 'int const *' to 'void *' is not allowed}}
(void)static_cast<A*>((E*)0); // expected-error {{inaccessible base class 'struct A'}}
(void)static_cast<A*>((E*)0); // expected-error {{private base class 'struct A'}}
(void)static_cast<A*>((H*)0); // expected-error {{ambiguous conversion}}
(void)static_cast<int>((int*)0); // expected-error {{static_cast from 'int *' to 'int' is not allowed}}
(void)static_cast<A**>((B**)0); // expected-error {{static_cast from 'struct B **' to 'struct A **' is not allowed}}
@ -86,8 +86,8 @@ void t_529_5_8()
(void)static_cast<D&>(*((A*)0)); // expected-error {{cannot cast 'struct A' to 'struct D &' via virtual base 'struct B'}}
(void)static_cast<B*>((const A*)0); // expected-error {{static_cast from 'struct A const *' to 'struct B *' casts away constness}}
(void)static_cast<B&>(*((const A*)0)); // expected-error {{static_cast from 'struct A const' to 'struct B &' casts away constness}}
(void)static_cast<E*>((A*)0); // expected-error {{cannot cast 'struct A' to 'struct E' due to inaccessible}}
(void)static_cast<E&>(*((A*)0)); // expected-error {{cannot cast 'struct A' to 'struct E' due to inaccessible}}
(void)static_cast<E*>((A*)0); // expected-error {{cannot cast private base class 'struct A' to 'struct E'}}
(void)static_cast<E&>(*((A*)0)); // expected-error {{cannot cast private base class 'struct A' to 'struct E'}}
(void)static_cast<H*>((A*)0); // expected-error {{ambiguous cast from base 'struct A' to derived 'struct H':\n struct A -> struct B -> struct G1 -> struct H\n struct A -> struct B -> struct G2 -> struct H}}
(void)static_cast<H&>(*((A*)0)); // expected-error {{ambiguous cast from base 'struct A' to derived 'struct H':\n struct A -> struct B -> struct G1 -> struct H\n struct A -> struct B -> struct G2 -> struct H}}
(void)static_cast<E*>((B*)0); // expected-error {{static_cast from 'struct B *' to 'struct E *' is not allowed}}

View File

@ -29,14 +29,14 @@ class B : A {
namespace T3 {
struct a { };
struct b : private a { }; // expected-note{{'private' inheritance specifier here}}
struct b : private a { }; // expected-note{{declared private here}}
class A {
virtual a* f(); // expected-note{{overridden virtual function is here}}
};
class B : A {
virtual b* f(); // expected-error{{return type of virtual function 'f' is not covariant with the return type of the function it overrides (conversion from 'struct T3::b' to inaccessible base class 'struct T3::a')}}
virtual b* f(); // expected-error{{invalid covariant return for virtual function: 'struct T3::a' is a private base class of 'struct T3::b'}}
};
}