diff options
author | John McCall <rjmccall@apple.com> | 2010-02-10 09:31:12 +0000 |
---|---|---|
committer | John McCall <rjmccall@apple.com> | 2010-02-10 09:31:12 +0000 |
commit | 6b2accb4793e16b2e93a8c2589f5df702231f17a (patch) | |
tree | 209f787cb3500bef39cb835d268624e3a69ba126 | |
parent | cf98c3f6b71d6a6ee754c100c2bb6991f28b8e09 (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.h | 11 | ||||
-rw-r--r-- | include/clang/Basic/DiagnosticSemaKinds.td | 24 | ||||
-rw-r--r-- | lib/Sema/Lookup.h | 2 | ||||
-rw-r--r-- | lib/Sema/Sema.h | 145 | ||||
-rw-r--r-- | lib/Sema/SemaAccess.cpp | 628 | ||||
-rw-r--r-- | lib/Sema/SemaCXXCast.cpp | 14 | ||||
-rw-r--r-- | lib/Sema/SemaDeclCXX.cpp | 23 | ||||
-rw-r--r-- | lib/Sema/SemaExceptionSpec.cpp | 18 | ||||
-rw-r--r-- | lib/Sema/SemaLookup.cpp | 2 | ||||
-rw-r--r-- | lib/Sema/SemaOverload.cpp | 16 | ||||
-rw-r--r-- | test/CXX/class.access/class.access.base/p1.cpp | 45 | ||||
-rw-r--r-- | test/CXX/class.access/p4.cpp | 34 | ||||
-rw-r--r-- | test/CXX/class.access/p6.cpp | 10 | ||||
-rw-r--r-- | test/CXX/class/class.local/p2.cpp | 4 | ||||
-rw-r--r-- | test/CXX/conv/conv.mem/p4.cpp | 8 | ||||
-rw-r--r-- | test/SemaCXX/access-base-class.cpp | 23 | ||||
-rw-r--r-- | test/SemaCXX/access-control-check.cpp | 2 | ||||
-rw-r--r-- | test/SemaCXX/conditional-expr.cpp | 10 | ||||
-rw-r--r-- | test/SemaCXX/static-cast.cpp | 8 | ||||
-rw-r--r-- | test/SemaCXX/virtual-override.cpp | 4 |
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 |