From 5b0829a321d56fe8349e3226efa66355d423c9e2 Mon Sep 17 00:00:00 2001 From: John McCall Date: Wed, 10 Feb 2010 09:31:12 +0000 Subject: [PATCH] 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 --- clang/include/clang/AST/CXXInheritance.h | 11 +- .../clang/Basic/DiagnosticSemaKinds.td | 24 +- clang/lib/Sema/Lookup.h | 2 +- clang/lib/Sema/Sema.h | 145 +++- clang/lib/Sema/SemaAccess.cpp | 656 +++++++++++------- clang/lib/Sema/SemaCXXCast.cpp | 14 +- clang/lib/Sema/SemaDeclCXX.cpp | 23 +- clang/lib/Sema/SemaExceptionSpec.cpp | 18 +- clang/lib/Sema/SemaLookup.cpp | 2 +- clang/lib/Sema/SemaOverload.cpp | 16 +- .../CXX/class.access/class.access.base/p1.cpp | 45 +- clang/test/CXX/class.access/p4.cpp | 34 +- clang/test/CXX/class.access/p6.cpp | 10 +- clang/test/CXX/class/class.local/p2.cpp | 4 +- clang/test/CXX/conv/conv.mem/p4.cpp | 8 +- clang/test/SemaCXX/access-base-class.cpp | 23 +- clang/test/SemaCXX/access-control-check.cpp | 2 +- clang/test/SemaCXX/conditional-expr.cpp | 10 +- clang/test/SemaCXX/static-cast.cpp | 8 +- clang/test/SemaCXX/virtual-override.cpp | 4 +- 20 files changed, 669 insertions(+), 390 deletions(-) diff --git a/clang/include/clang/AST/CXXInheritance.h b/clang/include/clang/AST/CXXInheritance.h index 1491b1edbbac..79a3022ee014 100644 --- a/clang/include/clang/AST/CXXInheritance.h +++ b/clang/include/clang/AST/CXXInheritance.h @@ -161,7 +161,8 @@ class CXXBasePaths { void ComputeDeclsFound(); public: - typedef std::list::const_iterator paths_iterator; + typedef std::list::iterator paths_iterator; + typedef std::list::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/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 05dca15caed7..705ec8552956 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/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/clang/lib/Sema/Lookup.h b/clang/lib/Sema/Lookup.h index fa6f03702648..6b2694591f52 100644 --- a/clang/lib/Sema/Lookup.h +++ b/clang/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/clang/lib/Sema/Sema.h b/clang/lib/Sema/Sema.h index 8b3b4650387a..a835c477e280 100644 --- a/clang/lib/Sema/Sema.h +++ b/clang/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 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(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/clang/lib/Sema/SemaAccess.cpp b/clang/lib/Sema/SemaAccess.cpp index 9e1ab8cefd31..eca8bb4c2a9b 100644 --- a/clang/lib/Sema/SemaAccess.cpp +++ b/clang/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"); +namespace { +struct EffectiveContext { + EffectiveContext() : Record(0), Function(0) {} - - const CXXBaseSpecifier *InaccessibleBase = 0; - - const CXXRecordDecl *CurrentClassDecl = 0; - if (CXXMethodDecl *MD = dyn_cast_or_null(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(DC)) { + Function = cast(DC); + DC = Function->getDeclContext(); + } else + Function = 0; + + if (isa(DC)) + Record = cast(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(D->getDeclContext()); + while (DeclaringClass->isAnonymousStructOrUnion()) + DeclaringClass = cast(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(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(DC) && - cast(DC)->isAnonymousStructOrUnion()) - DC = DC->getParent(); - - CXXRecordDecl *CurRecord; - if (isa(DC)) - CurRecord = cast(DC); - else if (isa(DC)) - CurRecord = cast(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(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(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(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(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(); assert(RT && "found member operator but object expr not of record type"); CXXRecordDecl *NamingClass = cast(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(Base->getAs()->getDecl()); + DerivedD = cast(Derived->getAs()->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)); } diff --git a/clang/lib/Sema/SemaCXXCast.cpp b/clang/lib/Sema/SemaCXXCast.cpp index 63823054d855..48258ff6ad6a 100644 --- a/clang/lib/Sema/SemaCXXCast.cpp +++ b/clang/lib/Sema/SemaCXXCast.cpp @@ -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; } diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 67c08225b4e0..b42a27cdcb2c 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -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())) { diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp b/clang/lib/Sema/SemaExceptionSpec.cpp index 7e2a98d0bf1f..d0718d020b2b 100644 --- a/clang/lib/Sema/SemaExceptionSpec.cpp +++ b/clang/lib/Sema/SemaExceptionSpec.cpp @@ -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; diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp index 8cbcf127a439..0d95c713b01c 100644 --- a/clang/lib/Sema/SemaLookup.cpp +++ b/clang/lib/Sema/SemaLookup.cpp @@ -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) diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index 452901cf225b..b79b1cc99375 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -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(); 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; diff --git a/clang/test/CXX/class.access/class.access.base/p1.cpp b/clang/test/CXX/class.access/class.access.base/p1.cpp index fd0d9f68e1e2..1bbcedb9a1e7 100644 --- a/clang/test/CXX/class.access/class.access.base/p1.cpp +++ b/clang/test/CXX/class.access/class.access.base/p1.cpp @@ -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}} } } diff --git a/clang/test/CXX/class.access/p4.cpp b/clang/test/CXX/class.access/p4.cpp index 83e467d9b963..7aa614cd8b1a 100644 --- a/clang/test/CXX/class.access/p4.cpp +++ b/clang/test/CXX/class.access/p4.cpp @@ -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}} } } diff --git a/clang/test/CXX/class.access/p6.cpp b/clang/test/CXX/class.access/p6.cpp index ce60b3bf04cd..aaf510a6d11f 100644 --- a/clang/test/CXX/class.access/p6.cpp +++ b/clang/test/CXX/class.access/p6.cpp @@ -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}} } } diff --git a/clang/test/CXX/class/class.local/p2.cpp b/clang/test/CXX/class/class.local/p2.cpp index 56ff1e53a493..8d281a57e137 100644 --- a/clang/test/CXX/class/class.local/p2.cpp +++ b/clang/test/CXX/class/class.local/p2.cpp @@ -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'}} } diff --git a/clang/test/CXX/conv/conv.mem/p4.cpp b/clang/test/CXX/conv/conv.mem/p4.cpp index d782cde25a42..1ecbc47ffbb8 100644 --- a/clang/test/CXX/conv/conv.mem/p4.cpp +++ b/clang/test/CXX/conv/conv.mem/p4.cpp @@ -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'}} } }; diff --git a/clang/test/SemaCXX/access-base-class.cpp b/clang/test/SemaCXX/access-base-class.cpp index f4c58d940b63..d0b0fb8ee223 100644 --- a/clang/test/SemaCXX/access-base-class.cpp +++ b/clang/test/SemaCXX/access-base-class.cpp @@ -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 + } + }; +} + diff --git a/clang/test/SemaCXX/access-control-check.cpp b/clang/test/SemaCXX/access-control-check.cpp index cf2d191a294f..783d4def5327 100644 --- a/clang/test/SemaCXX/access-control-check.cpp +++ b/clang/test/SemaCXX/access-control-check.cpp @@ -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'}} }; diff --git a/clang/test/SemaCXX/conditional-expr.cpp b/clang/test/SemaCXX/conditional-expr.cpp index b71133bfeec8..b961ff2de910 100644 --- a/clang/test/SemaCXX/conditional-expr.cpp +++ b/clang/test/SemaCXX/conditional-expr.cpp @@ -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'}} diff --git a/clang/test/SemaCXX/static-cast.cpp b/clang/test/SemaCXX/static-cast.cpp index d41b929ee6ab..4818b041ad08 100644 --- a/clang/test/SemaCXX/static-cast.cpp +++ b/clang/test/SemaCXX/static-cast.cpp @@ -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((const int*)0); // expected-error {{static_cast from 'int const *' to 'void *' is not allowed}} - (void)static_cast((E*)0); // expected-error {{inaccessible base class 'struct A'}} + (void)static_cast((E*)0); // expected-error {{private base class 'struct A'}} (void)static_cast((H*)0); // expected-error {{ambiguous conversion}} (void)static_cast((int*)0); // expected-error {{static_cast from 'int *' to 'int' is not allowed}} (void)static_cast((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(*((A*)0)); // expected-error {{cannot cast 'struct A' to 'struct D &' via virtual base 'struct B'}} (void)static_cast((const A*)0); // expected-error {{static_cast from 'struct A const *' to 'struct B *' casts away constness}} (void)static_cast(*((const A*)0)); // expected-error {{static_cast from 'struct A const' to 'struct B &' casts away constness}} - (void)static_cast((A*)0); // expected-error {{cannot cast 'struct A' to 'struct E' due to inaccessible}} - (void)static_cast(*((A*)0)); // expected-error {{cannot cast 'struct A' to 'struct E' due to inaccessible}} + (void)static_cast((A*)0); // expected-error {{cannot cast private base class 'struct A' to 'struct E'}} + (void)static_cast(*((A*)0)); // expected-error {{cannot cast private base class 'struct A' to 'struct E'}} (void)static_cast((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(*((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((B*)0); // expected-error {{static_cast from 'struct B *' to 'struct E *' is not allowed}} diff --git a/clang/test/SemaCXX/virtual-override.cpp b/clang/test/SemaCXX/virtual-override.cpp index 5e1e9b0899c6..6f1d83fd8ae8 100644 --- a/clang/test/SemaCXX/virtual-override.cpp +++ b/clang/test/SemaCXX/virtual-override.cpp @@ -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'}} }; }