aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn McCall <rjmccall@apple.com>2010-02-10 09:31:12 +0000
committerJohn McCall <rjmccall@apple.com>2010-02-10 09:31:12 +0000
commit6b2accb4793e16b2e93a8c2589f5df702231f17a (patch)
tree209f787cb3500bef39cb835d268624e3a69ba126
parentcf98c3f6b71d6a6ee754c100c2bb6991f28b8e09 (diff)
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. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@95775 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--include/clang/AST/CXXInheritance.h11
-rw-r--r--include/clang/Basic/DiagnosticSemaKinds.td24
-rw-r--r--lib/Sema/Lookup.h2
-rw-r--r--lib/Sema/Sema.h145
-rw-r--r--lib/Sema/SemaAccess.cpp628
-rw-r--r--lib/Sema/SemaCXXCast.cpp14
-rw-r--r--lib/Sema/SemaDeclCXX.cpp23
-rw-r--r--lib/Sema/SemaExceptionSpec.cpp18
-rw-r--r--lib/Sema/SemaLookup.cpp2
-rw-r--r--lib/Sema/SemaOverload.cpp16
-rw-r--r--test/CXX/class.access/class.access.base/p1.cpp45
-rw-r--r--test/CXX/class.access/p4.cpp34
-rw-r--r--test/CXX/class.access/p6.cpp10
-rw-r--r--test/CXX/class/class.local/p2.cpp4
-rw-r--r--test/CXX/conv/conv.mem/p4.cpp8
-rw-r--r--test/SemaCXX/access-base-class.cpp23
-rw-r--r--test/SemaCXX/access-control-check.cpp2
-rw-r--r--test/SemaCXX/conditional-expr.cpp10
-rw-r--r--test/SemaCXX/static-cast.cpp8
-rw-r--r--test/SemaCXX/virtual-override.cpp4
20 files changed, 655 insertions, 376 deletions
diff --git a/include/clang/AST/CXXInheritance.h b/include/clang/AST/CXXInheritance.h
index 1491b1edbb..79a3022ee0 100644
--- a/include/clang/AST/CXXInheritance.h
+++ b/include/clang/AST/CXXInheritance.h
@@ -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; }
diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td
index 05dca15cae..705ec85529 100644
--- a/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/include/clang/Basic/DiagnosticSemaKinds.td
@@ -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">;
diff --git a/lib/Sema/Lookup.h b/lib/Sema/Lookup.h
index fa6f037026..6b2694591f 100644
--- a/lib/Sema/Lookup.h
+++ b/lib/Sema/Lookup.h
@@ -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) {
diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h
index 8b3b465038..a835c477e2 100644
--- a/lib/Sema/Sema.h
+++ b/lib/Sema/Sema.h
@@ -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,
diff --git a/lib/Sema/SemaAccess.cpp b/lib/Sema/SemaAccess.cpp
index 9e1ab8cefd..eca8bb4c2a 100644
--- a/lib/Sema/SemaAccess.cpp
+++ b/lib/Sema/SemaAccess.cpp
@@ -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");
-
-
- 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;
- }
+namespace {
+struct EffectiveContext {
+ EffectiveContext() : Record(0), Function(0) {}
+
+ 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
+ Record = 0;
+ }
+
+ bool isClass(const CXXRecordDecl *R) const {
+ return R->getCanonicalDecl() == Record;
+ }
+
+ CXXRecordDecl *Record;
+ FunctionDecl *Function;
+};
+}
- if (FoundInaccessibleBase) {
- InaccessibleBase = Base;
- break;
+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);
}
- if (!FoundInaccessibleBase) {
- // We found a path to the base, our work here is done.
- return 0;
+ // 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;
}
}
- assert(InaccessibleBase && "no path found, but no inaccessible base");
- return InaccessibleBase;
+ return BestPath;
}
-/// 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) {
+/// Diagnose the path which caused the given declaration or base class
+/// to become inaccessible.
+static void DiagnoseAccessPath(Sema &S,
+ const EffectiveContext &EC,
+ CXXRecordDecl *NamingClass,
+ CXXRecordDecl *DeclaringClass,
+ NamedDecl *D, AccessSpecifier Access) {
+ // Easy case: the decl's natural access determined its path access.
+ // We have to check against AS_private here in case Access is AS_none,
+ // indicating a non-public member of a private base class.
+ //
+ // DependentFriend should be impossible here.
+ if (D && (Access == D->getAccess() || D->getAccess() == AS_private)) {
+ switch (GetFriendKind(S, EC, DeclaringClass)) {
+ case Sema::AR_inaccessible: {
+ S.Diag(D->getLocation(), diag::note_access_natural)
+ << (unsigned) (Access == AS_protected)
+ << /*FIXME: not implicitly*/ 0;
+ return;
+ }
- if (!getLangOptions().AccessControl)
- return false;
- const CXXBaseSpecifier *InaccessibleBase = FindInaccessibleBase(
- Derived, Base, Paths);
+ case Sema::AR_accessible: break;
- if (InaccessibleBase) {
- Diag(AccessLoc, InaccessibleBaseID)
- << Derived << Base << Name;
+ case Sema::AR_dependent:
+ case Sema::AR_delayed:
+ llvm_unreachable("dependent/delayed not allowed");
+ return;
+ }
+ }
- AccessSpecifier AS = InaccessibleBase->getAccessSpecifierAsWritten();
+ CXXBasePaths Paths;
+ CXXBasePath &Path = *FindBestPath(S, EC, NamingClass, DeclaringClass, Paths);
- // 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);
- else
- Diag(InaccessibleBase->getSourceRange().getBegin(),
- diag::note_inheritance_specifier_here) << AS;
+ CXXBasePath::iterator I = Path.end(), E = Path.begin();
+ while (I != E) {
+ --I;
- return true;
+ 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;
+ }
}
- return false;
+ llvm_unreachable("access not apparently constrained by path");
}
-/// Diagnose the path which caused the given declaration to become
-/// inaccessible.
-static void DiagnoseAccessPath(Sema &S, const LookupResult &R, 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);
- return;
+/// 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);
+}
- // TODO: flesh this out
- S.Diag(D->getLocation(), diag::note_access_constrained_by_path)
- << (unsigned) (Access == AS_protected);
+/// 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);
}
-/// 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");
+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);
+}
- // 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;
+/// 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");
+ }
+ }
- return CheckEffectiveAccess(CurContext, R, D, Access);
-}
+ if (DeclaringClass == NamingClass) {
+ Access = DeclAccess;
+ return;
+ }
+ }
-/// 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;
+ 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;
}
- CXXRecordDecl *NamingClass = R.getNamingClass();
+ // 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 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);
+
+ 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
-
- // Okay, it's a bad access, reject it.
+ // 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;
+}
-
- CXXRecordDecl *DeclaringClass = cast<CXXRecordDecl>(D-&g