diff options
Diffstat (limited to 'clang/lib')
-rw-r--r-- | clang/lib/AST/CXXInheritance.cpp | 42 | ||||
-rw-r--r-- | clang/lib/Parse/ParseDeclCXX.cpp | 2 | ||||
-rw-r--r-- | clang/lib/Sema/Lookup.h | 36 | ||||
-rw-r--r-- | clang/lib/Sema/Sema.h | 3 | ||||
-rw-r--r-- | clang/lib/Sema/SemaAccess.cpp | 99 | ||||
-rw-r--r-- | clang/lib/Sema/SemaDecl.cpp | 4 | ||||
-rw-r--r-- | clang/lib/Sema/SemaLookup.cpp | 12 |
7 files changed, 180 insertions, 18 deletions
diff --git a/clang/lib/AST/CXXInheritance.cpp b/clang/lib/AST/CXXInheritance.cpp index d575ccd9826..72083286926 100644 --- a/clang/lib/AST/CXXInheritance.cpp +++ b/clang/lib/AST/CXXInheritance.cpp @@ -61,7 +61,6 @@ void CXXBasePaths::clear() { Paths.clear(); ClassSubobjects.clear(); ScratchPath.clear(); - ScratchAccess.clear(); DetectedVirtual = 0; } @@ -147,6 +146,10 @@ bool CXXRecordDecl::lookupInBases(BaseMatchesCallback *BaseMatches, CXXBasePaths &Paths) const { bool FoundPath = false; + // The access of the path down to this record. + AccessSpecifier AccessToHere = Paths.ScratchPath.Access; + bool IsFirstStep = Paths.ScratchPath.empty(); + ASTContext &Context = getASTContext(); for (base_class_const_iterator BaseSpec = bases_begin(), BaseSpecEnd = bases_end(); BaseSpec != BaseSpecEnd; ++BaseSpec) { @@ -191,20 +194,30 @@ bool CXXRecordDecl::lookupInBases(BaseMatchesCallback *BaseMatches, Element.SubobjectNumber = Subobjects.second; Paths.ScratchPath.push_back(Element); - // C++0x [class.access.base]p1 (paraphrased): - // The access of a member of a base class is the less permissive - // of its access within the base class and the access of the base - // class within the derived class. - // We're just calculating the access along the path, so we ignore - // the access specifiers of whatever decls we've found. - AccessSpecifier PathAccess = Paths.ScratchPath.Access; - Paths.ScratchAccess.push_back(PathAccess); - Paths.ScratchPath.Access - = std::max(PathAccess, BaseSpec->getAccessSpecifier()); + // Calculate the "top-down" access to this base class. + // The spec actually describes this bottom-up, but top-down is + // equivalent because the definition works out as follows: + // 1. Write down the access along each step in the inheritance + // chain, followed by the access of the decl itself. + // For example, in + // class A { public: int foo; }; + // class B : protected A {}; + // class C : public B {}; + // class D : private C {}; + // we would write: + // private public protected public + // 2. If 'private' appears anywhere except far-left, access is denied. + // 3. Otherwise, overall access is determined by the most restrictive + // access in the sequence. + if (IsFirstStep) + Paths.ScratchPath.Access = BaseSpec->getAccessSpecifier(); + else + Paths.ScratchPath.Access + = MergeAccess(AccessToHere, BaseSpec->getAccessSpecifier()); } if (BaseMatches(BaseSpec, Paths.ScratchPath, UserData)) { - // We've found a path that terminates that this base. + // We've found a path that terminates at this base. FoundPath = true; if (Paths.isRecordingPaths()) { // We have a path. Make a copy of it before moving on. @@ -237,8 +250,6 @@ bool CXXRecordDecl::lookupInBases(BaseMatchesCallback *BaseMatches, // collecting paths). if (Paths.isRecordingPaths()) { Paths.ScratchPath.pop_back(); - Paths.ScratchPath.Access = Paths.ScratchAccess.back(); - Paths.ScratchAccess.pop_back(); } // If we set a virtual earlier, and this isn't a path, forget it again. @@ -246,6 +257,9 @@ bool CXXRecordDecl::lookupInBases(BaseMatchesCallback *BaseMatches, Paths.DetectedVirtual = 0; } } + + // Reset the scratch path access. + Paths.ScratchPath.Access = AccessToHere; return FoundPath; } diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp index a5ef817d43d..efaf8ee3270 100644 --- a/clang/lib/Parse/ParseDeclCXX.cpp +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -983,7 +983,7 @@ Parser::BaseResult Parser::ParseBaseSpecifier(DeclPtrTy ClassDecl) { // Parse an (optional) access specifier. AccessSpecifier Access = getAccessSpecifierIfPresent(); - if (Access) + if (Access != AS_none) ConsumeToken(); // Parse the 'virtual' keyword (again!), in case it came after the diff --git a/clang/lib/Sema/Lookup.h b/clang/lib/Sema/Lookup.h index 274a3dabbf0..f4cea2e88a6 100644 --- a/clang/lib/Sema/Lookup.h +++ b/clang/lib/Sema/Lookup.h @@ -131,6 +131,7 @@ public: Sema::RedeclarationKind Redecl = Sema::NotForRedeclaration) : ResultKind(NotFound), Paths(0), + NamingClass(0), SemaRef(SemaRef), Name(Name), NameLoc(NameLoc), @@ -150,6 +151,7 @@ public: LookupResult(TemporaryToken _, const LookupResult &Other) : ResultKind(NotFound), Paths(0), + NamingClass(0), SemaRef(Other.SemaRef), Name(Other.Name), NameLoc(Other.NameLoc), @@ -245,6 +247,37 @@ public: return IDNS; } + /// \brief Returns whether these results arose from performing a + /// lookup into a class. + bool isClassLookup() const { + return NamingClass != 0; + } + + /// \brief Returns the 'naming class' for this lookup, i.e. the + /// class which was looked into to find these results. + /// + /// C++0x [class.access.base]p5: + /// The access to a member is affected by the class in which the + /// member is named. This naming class is the class in which the + /// member name was looked up and found. [Note: this class can be + /// explicit, e.g., when a qualified-id is used, or implicit, + /// e.g., when a class member access operator (5.2.5) is used + /// (including cases where an implicit "this->" is added). If both + /// a class member access operator and a qualified-id are used to + /// name the member (as in p->T::m), the class naming the member + /// is the class named by the nested-name-specifier of the + /// qualified-id (that is, T). -- end note ] + /// + /// This is set by the lookup routines when they find results in a class. + CXXRecordDecl *getNamingClass() const { + return NamingClass; + } + + /// \brief Sets the 'naming class' for this lookup. + void setNamingClass(CXXRecordDecl *Record) { + NamingClass = Record; + } + /// \brief Add a declaration to these results with its natural access. /// Does not test the acceptance criteria. void addDecl(NamedDecl *D) { @@ -465,6 +498,8 @@ private: void diagnose() { if (isAmbiguous()) SemaRef.DiagnoseAmbiguousLookup(*this); + else if (isClassLookup() && SemaRef.getLangOptions().AccessControl) + SemaRef.CheckAccess(*this); } void setAmbiguous(AmbiguityKind AK) { @@ -504,6 +539,7 @@ private: AmbiguityKind Ambiguity; // ill-defined unless ambiguous UnresolvedSet<8> Decls; CXXBasePaths *Paths; + CXXRecordDecl *NamingClass; // Parameters. Sema &SemaRef; diff --git a/clang/lib/Sema/Sema.h b/clang/lib/Sema/Sema.h index 90a755aa0cc..06e9e3ae9e6 100644 --- a/clang/lib/Sema/Sema.h +++ b/clang/lib/Sema/Sema.h @@ -2361,6 +2361,9 @@ public: CXXBasePaths &Paths, bool NoPrivileges = false); + void CheckAccess(const LookupResult &R); + bool CheckAccess(const LookupResult &R, NamedDecl *D, AccessSpecifier Access); + bool CheckBaseClassAccess(QualType Derived, QualType Base, unsigned InaccessibleBaseID, CXXBasePaths& Paths, SourceLocation AccessLoc, diff --git a/clang/lib/Sema/SemaAccess.cpp b/clang/lib/Sema/SemaAccess.cpp index b7cc37b6c9a..51f9e300ef9 100644 --- a/clang/lib/Sema/SemaAccess.cpp +++ b/clang/lib/Sema/SemaAccess.cpp @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "Sema.h" +#include "Lookup.h" #include "clang/AST/ASTContext.h" #include "clang/AST/CXXInheritance.h" #include "clang/AST/DeclCXX.h" @@ -137,3 +138,101 @@ bool Sema::CheckBaseClassAccess(QualType Derived, QualType Base, return false; } + +/// 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; + } + + // TODO: flesh this out + S.Diag(D->getLocation(), diag::note_access_constrained_by_path) + << (unsigned) (Access == AS_protected); +} + +/// 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"); + + // If the access path is public, it's accessible everywhere. + if (Access == AS_public) + return false; + + // Otherwise, derive the current class context. + DeclContext *DC = CurContext; + while (isa<CXXRecordDecl>(DC) && + cast<CXXRecordDecl>(DC)->isAnonymousStructOrUnion()) + DC = DC->getParent(); + + CXXRecordDecl *CurRecord; + if (isa<CXXRecordDecl>(DC)) + CurRecord = cast<CXXRecordDecl>(DC); + else if (isa<CXXMethodDecl>(DC)) + CurRecord = cast<CXXMethodDecl>(DC)->getParent(); + else { + Diag(R.getNameLoc(), diag::err_access_outside_class) + << (Access == AS_protected); + DiagnoseAccessPath(*this, R, D, Access); + return true; + } + + CXXRecordDecl *NamingClass = R.getNamingClass(); + 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); + + // White-list accesses from within the declaring class. + if (Access != AS_none && + CurRecord->getCanonicalDecl() == NamingClass->getCanonicalDecl()) + return false; + + // Protected access. + if (Access == AS_protected) { + // FIXME: implement [class.protected]p1 + if (CurRecord->isDerivedFrom(NamingClass)) + return false; + + // FIXME: dependent classes + } + + // FIXME: friends + + // Okay, it's a bad access, reject it. + + + CXXRecordDecl *DeclaringClass = cast<CXXRecordDecl>(D->getDeclContext()); + + if (Access == AS_protected) { + Diag(R.getNameLoc(), diag::err_access_protected) + << Context.getTypeDeclType(DeclaringClass) + << Context.getTypeDeclType(CurRecord); + DiagnoseAccessPath(*this, R, D, Access); + return true; + } + + 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; +} + +/// Checks access to all the declarations in the given result set. +void Sema::CheckAccess(const LookupResult &R) { + for (LookupResult::iterator I = R.begin(), E = R.end(); I != E; ++I) + CheckAccess(R, *I, I.getAccess()); +} diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 4066a646a5f..fbe02894ace 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -5732,8 +5732,10 @@ Sema::DeclPtrTy Sema::ActOnEnumConstant(Scope *S, DeclPtrTy theEnumDecl, IdLoc, Id, Owned(Val)); // Register this decl in the current scope stack. - if (New) + if (New) { + New->setAccess(TheEnumDecl->getAccess()); PushOnScopeChains(New, S); + } return DeclPtrTy::make(New); } diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp index ddce4a4c23d..f5d2a7d8998 100644 --- a/clang/lib/Sema/SemaLookup.cpp +++ b/clang/lib/Sema/SemaLookup.cpp @@ -967,6 +967,8 @@ bool Sema::LookupQualifiedName(LookupResult &R, DeclContext *LookupCtx, // Perform qualified name lookup into the LookupCtx. if (LookupDirect(R, LookupCtx)) { R.resolveKind(); + if (isa<CXXRecordDecl>(LookupCtx)) + R.setNamingClass(cast<CXXRecordDecl>(LookupCtx)); return true; } @@ -1039,6 +1041,8 @@ bool Sema::LookupQualifiedName(LookupResult &R, DeclContext *LookupCtx, R.getLookupName().getAsOpaquePtr(), Paths)) return false; + R.setNamingClass(LookupRec); + // C++ [class.member.lookup]p2: // [...] If the resulting set of declarations are not all from // sub-objects of the same type, or the set has a nonstatic member @@ -1111,8 +1115,12 @@ bool Sema::LookupQualifiedName(LookupResult &R, DeclContext *LookupCtx, // Lookup in a base class succeeded; return these results. DeclContext::lookup_iterator I, E; - for (llvm::tie(I,E) = Paths.front().Decls; I != E; ++I) - R.addDecl(*I, std::max(SubobjectAccess, (*I)->getAccess())); + for (llvm::tie(I,E) = Paths.front().Decls; I != E; ++I) { + NamedDecl *D = *I; + AccessSpecifier AS = CXXRecordDecl::MergeAccess(SubobjectAccess, + D->getAccess()); + R.addDecl(D, AS); + } R.resolveKind(); return true; } |