forked from OSchip/llvm-project
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:
parent
f22553a1c7
commit
5b0829a321
|
@ -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; }
|
||||
|
|
|
@ -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">;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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())) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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}}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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}}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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}}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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'}}
|
||||
}
|
||||
|
|
|
@ -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'}}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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'}}
|
||||
};
|
||||
|
|
|
@ -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'}}
|
||||
|
||||
|
|
|
@ -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}}
|
||||
|
|
|
@ -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'}}
|
||||
};
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue